ecmwf-atlas-b0f79ee/0000775000175000017500000000000015263665111014612 5ustar alastairalastairecmwf-atlas-b0f79ee/bamboo/0000775000175000017500000000000015263665111016051 5ustar alastairalastairecmwf-atlas-b0f79ee/bamboo/CLANG-flags.cmake0000664000175000017500000000052315263665111020771 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 ecmwf-atlas-b0f79ee/bamboo/env.sh0000664000175000017500000000006315263665111017174 0ustar alastairalastair#!/usr/bin/env bash # export ctest_parallel="no" ecmwf-atlas-b0f79ee/bamboo/INTEL-flags.cmake0000664000175000017500000000047015263665111021021 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 ecmwf-atlas-b0f79ee/bamboo/CLANG-env.sh0000664000175000017500000000104415263665111020016 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 ecmwf-atlas-b0f79ee/bamboo/CMakeLists.txt0000664000175000017500000000025415263665111020612 0ustar alastairalastairfile( GLOB_RECURSE bamboo_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*" ) ecbuild_add_resources( TARGET ${PROJECT_NAME}_bamboo SOURCES_DONT_PACK ${bamboo_files} ) ecmwf-atlas-b0f79ee/bamboo/INTEL-env.sh0000664000175000017500000000066215263665111020052 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 ecmwf-atlas-b0f79ee/bamboo/flags.cmake0000664000175000017500000000147015263665111020151 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") ecmwf-atlas-b0f79ee/bamboo/MACOSX-env.sh0000664000175000017500000000007515263665111020167 0ustar alastairalastairexport PATH=$HOME/Applications/CMake.app/Contents/bin:$PATH ecmwf-atlas-b0f79ee/bamboo/leap42-env.sh0000775000175000017500000000010415263665111020260 0ustar alastairalastair#!/bin/bash # CC=mpicc # CXX=mpicxx # FC=mpif90 ulimit -s unlimitedecmwf-atlas-b0f79ee/bamboo/GCC-env.sh0000664000175000017500000000065115263665111017571 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 ecmwf-atlas-b0f79ee/bamboo/GCC-flags.cmake0000664000175000017500000000073415263665111020545 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" ) ecmwf-atlas-b0f79ee/.github/0000775000175000017500000000000015263665111016152 5ustar alastairalastairecmwf-atlas-b0f79ee/.github/ci-hpc-config.yml0000664000175000017500000000120215263665111021276 0ustar alastairalastairmatrix: - mpi_on - mpi_off mpi_on: build: modules: - ninja modules_package: - atlas:fftw,eigen/3.4.1,qhull,python3/3.12,openmpi - fckit:python3/3.12,openmpi - eckit:openmpi dependencies: - ecmwf/ecbuild@develop - ecmwf/eckit@develop - ecmwf/fckit@develop parallel: 64 ntasks: 16 env: - ATLAS_FPE=0 mpi_off: build: modules: - ninja modules_package: - atlas:fftw,eigen/3.4.1,qhull,python3/3.12 - fckit:python3/3.12 dependencies: - ecmwf/ecbuild@develop - ecmwf/eckit@develop - ecmwf/fckit@develop parallel: 64 ecmwf-atlas-b0f79ee/.github/ci-config.yml0000664000175000017500000000015515263665111020534 0ustar alastairalastairdependencies: | ecmwf/ecbuild ecmwf/eckit ecmwf/fckit dependency_branch: develop parallelism_factor: 8 ecmwf-atlas-b0f79ee/.github/static-analyzer-skip.txt0000664000175000017500000000034015263665111022766 0ustar alastairalastair# This file can be used to suppress warnings # For syntax, see https://codechecker.readthedocs.io/en/latest/analyzer/user_guide/#skip-file # Avoid warnings from external pocket_hdronly.h -*/atlas/linalg/fft/pocketfft.cc ecmwf-atlas-b0f79ee/.github/workflows/0000775000175000017500000000000015263665111020207 5ustar alastairalastairecmwf-atlas-b0f79ee/.github/workflows/publish.yml0000664000175000017500000000670015263665111022403 0ustar alastairalastairname: publish on: # Publish the Rust crate when a GitHub release is published release: types: [published] # Trigger the workflow manually (dry-run by default) workflow_dispatch: inputs: dry-run: description: Run cargo publish in dry-run mode only. type: boolean default: true jobs: check-version: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Verify release tag matches crate version and VERSION file if: github.event_name == 'release' env: PRERELEASE: ${{ github.event.release.prerelease }} run: | tag="${GITHUB_REF_NAME#v}" versions=$(cargo metadata --manifest-path rust/Cargo.toml --format-version 1 --no-deps | jq -r '.packages[].version' | sort -u) if [ "$(wc -l <<<"${versions}")" -ne 1 ]; then echo "::error::Workspace crates have differing versions: $(tr '\n' ' ' <<<"${versions}")" exit 1 fi if [ "${versions}" != "${tag}" ]; then echo "::error::Release tag ${GITHUB_REF_NAME} does not match crate version ${versions}." exit 1 fi file_version=$(tr -d '[:space:]' < VERSION) if [ "${versions}" != "${file_version}" ]; then echo "::error::Crate version ${versions} does not match VERSION file ${file_version}." exit 1 fi if [ "${PRERELEASE}" = "true" ] && [[ "${versions}" != *-* ]]; then echo "::error::GitHub pre-release ${GITHUB_REF_NAME} would publish final crate version ${versions}." exit 1 fi if [ "${PRERELEASE}" != "true" ] && [[ "${versions}" == *-* ]]; then echo "::error::GitHub release ${GITHUB_REF_NAME} would publish pre-release crate version ${versions}." exit 1 fi checks: runs-on: ubuntu-latest # Cargo resolves `.cargo/config.toml` against the working directory, not # the manifest — run from `rust/` or the workspace lint flags are lost. defaults: run: working-directory: rust steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup Rust toolchain uses: dtolnay/rust-toolchain@master with: toolchain: stable components: rustfmt, clippy - name: Cache cargo artifacts uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: workspaces: rust - name: Run formatting check run: cargo fmt --all --check # Default features only: --all-features would enable the mutually # exclusive vendored/system build strategies and need MPI, OpenMP, # Qhull, Eigen and FFTW on the runner. - name: Run clippy run: cargo clippy --workspace --all-targets -- -D warnings - name: Run tests run: cargo test --workspace - name: Dry-run publish run: cargo publish --dry-run --workspace publish-atlas-sys: if: github.event_name == 'release' || !inputs.dry-run needs: [check-version, checks] uses: ecmwf/reusable-workflows/.github/workflows/publish-rust-crate.yml@v2 with: manifest-path: rust/crates/atlas-sys/Cargo.toml run-checks: false locked: false secrets: cargo_registry_token: ${{ secrets.CARGO_REGISTRY_TOKEN }} ecmwf-atlas-b0f79ee/.github/workflows/ci.yml0000664000175000017500000000722515263665111021333 0ustar alastairalastairname: ci on: # Trigger the workflow on push to master or develop, except tag creation push: branches: - 'master' - 'develop' - 'ci' tags-ignore: - '**' # Trigger the workflow on pull request pull_request: ~ # Trigger the workflow manually workflow_dispatch: ~ # Trigger after public PR approved for CI pull_request_target: types: [labeled] jobs: # Run Rust CI (fmt + clippy + doc + test) on the rust/ workspace ci-rust: name: ci-rust if: ${{ !github.event.pull_request.head.repo.fork && github.event.action != 'labeled' || github.event.label.name == 'approved-for-ci' }} uses: ecmwf/reusable-workflows/.github/workflows/ci-rust.yml@v2 with: manifest-path: rust/Cargo.toml features: --features vendored # Run CI including downstream packages on self-hosted runners downstream-ci: name: downstream-ci if: ${{ !github.event.pull_request.head.repo.fork && github.event.action != 'labeled' || github.event.label.name == 'approved-for-ci' }} uses: ecmwf/downstream-ci/.github/workflows/downstream-ci.yml@main with: atlas: ecmwf/atlas@${{ github.event.pull_request.head.sha || github.sha }} secrets: inherit # Run CI of private downstream packages on self-hosted runners private-downstream-ci: name: private-downstream-ci needs: [downstream-ci] if: ${{ (success() || failure()) && (!github.event.pull_request.head.repo.fork && github.event.action != 'labeled' || github.event.label.name == 'approved-for-ci') }} runs-on: ubuntu-latest permissions: pull-requests: write steps: - name: Dispatch private downstream CI uses: ecmwf/dispatch-private-downstream-ci@v1 with: token: ${{ secrets.GH_REPO_READ_TOKEN }} owner: ecmwf repository: private-downstream-ci event_type: downstream-ci payload: '{"atlas": "ecmwf/atlas@${{ github.event.pull_request.head.sha || github.sha }}"}' # Build downstream packages on HPC downstream-ci-hpc: name: downstream-ci-hpc if: ${{ !github.event.pull_request.head.repo.fork && github.event.action != 'labeled' || github.event.label.name == 'approved-for-ci' }} uses: ecmwf/downstream-ci/.github/workflows/downstream-ci-hpc.yml@main with: atlas: ecmwf/atlas@${{ github.event.pull_request.head.sha || github.sha }} secrets: inherit # Run CI of private downstream packages on HPC private-downstream-ci-hpc: name: private-downstream-ci-hpc needs: [downstream-ci-hpc] if: ${{ (success() || failure()) && (!github.event.pull_request.head.repo.fork && github.event.action != 'labeled' || github.event.label.name == 'approved-for-ci') }} runs-on: ubuntu-latest permissions: pull-requests: write steps: - name: Dispatch private downstream CI uses: ecmwf/dispatch-private-downstream-ci@v1 with: token: ${{ secrets.GH_REPO_READ_TOKEN }} owner: ecmwf repository: private-downstream-ci event_type: downstream-ci-hpc payload: '{"atlas": "ecmwf/atlas@${{ github.event.pull_request.head.sha || github.sha }}"}' notify: runs-on: ubuntu-latest needs: - ci-rust - downstream-ci - private-downstream-ci - downstream-ci-hpc - private-downstream-ci-hpc if: ${{ always() && !github.event.pull_request.head.repo.fork && github.event.action != 'labeled' || github.event.label.name == 'approved-for-ci' }} steps: - name: Trigger Teams notification uses: ecmwf/notify-teams@v1 with: incoming_webhook: ${{ secrets.MS_TEAMS_INCOMING_WEBHOOK }} needs_context: ${{ toJSON(needs) }} ecmwf-atlas-b0f79ee/.github/workflows/notify-new-issue.yml0000664000175000017500000000042515263665111024160 0ustar alastairalastairname: Notify new issue on: issues: types: - "opened" jobs: notify: runs-on: ubuntu-latest steps: - name: Notify new issue uses: ecmwf/notify-teams-issue@v1 with: incoming_webhook: ${{ secrets.MS_TEAMS_INCOMING_WEBHOOK }} ecmwf-atlas-b0f79ee/.github/workflows/notify-new-pr.yml0000664000175000017500000000053615263665111023454 0ustar alastairalastairname: Notify new PR # Needs the worklow to be located in the branche the PR is merged to on: pull_request_target: types: - "opened" jobs: notify: runs-on: ubuntu-latest steps: - name: Notify new PR uses: ecmwf/notify-teams-pr@v1 with: incoming_webhook: ${{ secrets.MS_TEAMS_INCOMING_WEBHOOK }} ecmwf-atlas-b0f79ee/.github/workflows/check-release-version.yml0000664000175000017500000000030715263665111025110 0ustar alastairalastairname: Check VERSION file on: push: branches: - "release/**" - "hotfix/**" jobs: check_version: uses: ecmwf/reusable-workflows/.github/workflows/check-release-version.yml@v2 ecmwf-atlas-b0f79ee/.github/workflows/docs.yml0000664000175000017500000000034215263665111021661 0ustar alastairalastairname: docs on: push: tags: - '**' workflow_dispatch: ~ jobs: publish: uses: ecmwf/reusable-workflows/.github/workflows/cd-docs.yml@v2 secrets: inherit with: config: .github/docs-config.yml ecmwf-atlas-b0f79ee/.github/workflows/build-hpc.yml0000664000175000017500000001771715263665111022616 0ustar alastairalastairname: build-hpc # Controls when the action will run on: # Trigger the workflow on all pushes to main and develop, except on tag creation push: branches: - main - develop - ci tags-ignore: - '**' # Trigger the workflow on all pull requests pull_request: ~ # Allow workflow to be dispatched on demand workflow_dispatch: ~ # Trigger after public PR approved for CI pull_request_target: types: [labeled] env: ATLAS_TOOLS: ${{ github.workspace }}/tools CTEST_PARALLEL_LEVEL: 1 CACHE_SUFFIX: v1 # Increase to force new cache to be created jobs: ci-hpc: name: ci-hpc if: ${{ !github.event.pull_request.head.repo.fork && github.event.action != 'labeled' || github.event.label.name == 'approved-for-ci' }} strategy: fail-fast: false # false: try to complete all jobs matrix: name: - ac-gpu nvhpc include: - name: ac-gpu nvhpc site: ac-batch troika_user_secret: HPC_CI_GPU_SSH_USER sbatch_options: | #SBATCH --time=00:20:00 #SBATCH --nodes=1 #SBATCH --ntasks=4 #SBATCH --cpus-per-task=32 #SBATCH --gpus-per-task=1 #SBATCH --mem=200G #SBATCH --qos=dg modules: - cmake - ninja - prgenv/nvidia - hpcx-openmpi/2.14.0-cuda - fftw - qhull - eigen cmake_options: -DMPI_SLOTS=4 -DENABLE_WARNING_AS_ERROR=ON -DENABLE_PMR=OFF # ENABLE_PMR=OFF because the default host-compiler (gcc 8.5) is not std::pmr capable runs-on: [self-hosted, linux, hpc] env: GH_TOKEN: ${{ github.token }} steps: - uses: ecmwf/reusable-workflows/ci-hpc-generic@v2 with: site: ${{ matrix.site }} troika_user: ${{ secrets[matrix.troika_user_secret] }} sbatch_options: ${{ matrix.sbatch_options }} output_dir: ${{ matrix.output_dir || '' }} workdir: ${{ matrix.workdir || '' }} template_data: | cmake_options: - -DENABLE_MPI=ON - -DENABLE_ACC=ON - -DENABLE_CUDA=ON - -DSKIP_TEST_atlas_test_field_foreach=TRUE - ${{ matrix.cmake_options || '' }} ctest_options: ${{ matrix.ctest_options || '' }} dependencies: ecmwf/ecbuild: version: develop ecmwf/eckit: version: develop cmake_options: - -DENABLE_MPI=ON - -DENABLE_CUDA=OFF - -DENABLE_TESTS=OFF - -DENABLE_ECKIT_SQL=OFF - -DENABLE_ECKIT_CMD=OFF - -DENABLE_AIO=OFF - -DENABLE_ECKIT_GEO=OFF - -DENABLE_WARNINGS=OFF - ${{ matrix.cmake_options || '' }} ecmwf/fckit: version: develop cmake_options: - -DENABLE_TESTS=OFF - ${{ matrix.cmake_options || '' }} ecmwf-ifs/fiat: version: develop cmake_options: - -DENABLE_MPI=ON - -DENABLE_TESTS=OFF - ${{ matrix.cmake_options || '' }} ecmwf-ifs/ectrans: version: develop cmake_options: - -DENABLE_MPI=ON - -DENABLE_ACC=ON - -DENABLE_GPU=ON - -DENABLE_TESTS=OFF - ${{ matrix.cmake_options || '' }} template: | set +x module_load() { echo "+ module load $1" module load $1 } {% for module in "${{ join(matrix.modules, ',') }}".split(',') %} module_load {{module}} {% endfor %} echo "+ module list" module list BASEDIR=$PWD export CMAKE_TEST_LAUNCHER="srun;-n;1" export CMAKE_PREFIX_PATH=$BASEDIR/install:$CMAKE_PREFIX_PATH {% for repo_name, options in dependencies.items() %} name=$(basename {{repo_name}}) echo "::group::Get dependency $name" echo "+ mkdir -p $name" mkdir -p $name echo "+ pushd $name" pushd $name echo "+ git init" git init echo "+ git remote add origin ${{ github.server_url }}/{{repo_name}}" git remote add origin ${{ github.server_url }}/{{repo_name}} echo "+ git fetch origin {{options['version']}}" git fetch origin {{options['version']}} echo "+ git reset --hard FETCH_HEAD" git reset --hard FETCH_HEAD echo "+ cmake -G Ninja -S . -B build {{ options['cmake_options']|join(' ') }}" cmake -G Ninja -S . -B build {{ options['cmake_options']|join(' ') }} start=`date +%s` echo "+ cmake --build build" cmake --build build end=`date +%s` runtime=$((end-start)) echo "Build $name took $runtime seconds" echo "+ cmake --install build --prefix \$BASEDIR/install/$name" cmake --install build --prefix $BASEDIR/install/$name echo "+ export PATH=\$BASEDIR/install/$name/bin:\$PATH" export PATH=$BASEDIR/install/$name/bin:$PATH echo "+ popd" popd echo "::endgroup::" {% endfor %} REPO=${{ github.event.pull_request.head.repo.full_name || github.repository }} SHA=${{ github.event.pull_request.head.sha || github.sha }} name=$(basename $REPO) echo "::group::Checkout $name" echo "+ mkdir -p $name" mkdir -p $name echo "+ pushd $name" pushd $name echo "+ git init" git init echo "+ git remote add origin ${{ github.server_url }}/$REPO" git remote add origin ${{ github.server_url }}/$REPO echo "+ git fetch origin $SHA" git fetch origin $SHA echo "+git reset --hard FETCH_HEAD" git reset --hard FETCH_HEAD echo "+ popd" popd echo "::endgroup::" echo "::group::Build $name" echo "+ cmake -G Ninja -S $name -B build {{ cmake_options|join(' ') }}" cmake -G Ninja -S $name -B build {{ cmake_options|join(' ') }} start=`date +%s` echo "+ cmake --build build" cmake --build build end=`date +%s` runtime=$((end-start)) echo "Build $name took $runtime seconds" echo "::endgroup::" echo "::group::Test $name" export ATLAS_FINALISES_MPI=1 export ECKIT_MPI_WORKAROUND_SEGV_AT_EXIT=1 echo "+ ctest --test-dir build --output-on-failure {{ ctest_options }}" ctest --test-dir build --output-on-failure {{ ctest_options }} echo "::endgroup::" echo "::group::Install $name" echo "+ cmake --install build --prefix $BASEDIR/install/$name" cmake --install build --prefix $BASEDIR/install/$name echo "+ export PATH=\$BASEDIR/install/$name/bin:\$PATH" export PATH=$BASEDIR/install/$name/bin:$PATH echo "::endgroup::" echo "::group::Verify $name installation" echo "+ atlas --info" atlas --info echo "::endgroup::" echo "::group::Cleanup" set -x rm -r $name rm -r build rm -r $BASEDIR/install {% for repo_name in dependencies.keys() %} name=$(basename {{repo_name}}) rm -r $name {% endfor %} echo "::endgroup::" set +x ecmwf-atlas-b0f79ee/.github/workflows/build-wheel-wrapper.yml0000664000175000017500000000217615263665111024617 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. name: Build Python Wrapper Wheel on: # Trigger the workflow manually workflow_dispatch: inputs: use_test_pypi: description: Use test pypi instead of the regular one required: false type: boolean default: false # Allow to be called from another workflow -- eg `cd.yml` workflow_call: inputs: use_test_pypi: description: Use test pypi instead of the regular one required: false type: boolean default: false jobs: python-wrapper-wheel: name: Python Wrapper Wheel uses: ecmwf/reusable-workflows/.github/workflows/python-wrapper-wheel.yml@main with: use_test_pypi: ${{ inputs.use_test_pypi }} wheel_directory: python/atlaslib-ecmwf secrets: inherit ecmwf-atlas-b0f79ee/.github/workflows/label-public-pr.yml0000664000175000017500000000034315263665111023704 0ustar alastairalastair# Manage labels of pull requests that originate from forks name: label-public-pr on: pull_request_target: types: [opened, synchronize] jobs: label: uses: ecmwf/reusable-workflows/.github/workflows/label-pr.yml@v2 ecmwf-atlas-b0f79ee/.github/workflows/build.yml0000664000175000017500000003272015263665111022035 0ustar alastairalastairname: build # Controls when the action will run on: # Trigger the workflow on all pushes, except on tag creation push: branches: - '**' tags-ignore: - '**' # Trigger the workflow on all pull requests pull_request: ~ # Allow workflow to be dispatched on demand workflow_dispatch: ~ env: ATLAS_TOOLS: ${{ github.workspace }}/tools CTEST_PARALLEL_LEVEL: 1 CACHE_SUFFIX: v3 jobs: ci: name: ci strategy: fail-fast: false # false: try to complete all jobs matrix: build_type: [Release,Debug] name: - linux gnu-10 - linux gnu-12 - linux gnu-14 - linux clang-13 - linux clang-18 - linux intel-2025.3 - linux intel-classic-2021.10 - linux nvhpc-26.1 - macos include: - name: linux gnu-10 os: ubuntu-22.04 compiler: gnu-10 compiler_cc: gcc-10 compiler_cxx: g++-10 compiler_fc: gfortran-10 caching: true coverage: true - name: linux gnu-12 os: ubuntu-24.04 compiler: gnu-12 compiler_cc: gcc-12 compiler_cxx: g++-12 compiler_fc: gfortran-12 caching: true coverage: false - name: linux gnu-14 os: ubuntu-24.04 compiler: gnu-14 compiler_cc: gcc-14 compiler_cxx: g++-14 compiler_fc: gfortran-14 caching: true coverage: false - name: linux clang-13 os: ubuntu-22.04 compiler: clang-13 compiler_cc: clang-13 compiler_cxx: clang++-13 compiler_fc: gfortran-10 caching: true coverage: false - name: linux clang-18 os: ubuntu-24.04 compiler: clang-18 compiler_cc: clang-18 compiler_cxx: clang++-18 compiler_fc: gfortran-14 caching: true coverage: false - name: linux intel-2025.3 os: ubuntu-24.04 compiler: intel-2025.3 compiler_cc: icx compiler_cxx: icpx compiler_fc: ifx caching: true coverage: false - name: linux intel-classic-2021.10 os: ubuntu-22.04 compiler: intel-classic-2021.10 compiler_cc: icc compiler_cxx: icpc compiler_fc: ifort caching: true coverage: false - name: linux nvhpc-26.1 os: ubuntu-24.04 compiler: nvhpc-26.1 compiler_cc: nvc compiler_cxx: nvc++ compiler_fc: nvfortran caching: true coverage: false cmake_options: -DSKIP_TEST_atlas_test_omp_sort=TRUE - name: macos # Xcode compiler requires empty environment variables, so we pass null (~) here os: macos-15 compiler: clang-17 compiler_cc: ~ compiler_cxx: ~ compiler_fc: gfortran-15 caching: true coverage: false cmake_options: -DMPI_SLOTS=4 runs-on: ${{ matrix.os }} steps: - name: System info shell: bash -eux {0} run: | if [[ "${{ matrix.os }}" =~ macos ]]; then system_profiler SPHardwareDataType -detailLevel mini sysctl machdep.cpu else sed '/^$/q' /proc/cpuinfo fi - name: Checkout Repository uses: actions/checkout@v6 - name: Set up Homebrew if: contains( matrix.os, 'macos' ) id: set-up-homebrew uses: Homebrew/actions/setup-homebrew@main - name: Environment run: | echo "DEPS_DIR=${{ runner.temp }}/deps" >> $GITHUB_ENV echo "CC=${{ matrix.compiler_cc }}" >> $GITHUB_ENV echo "CXX=${{ matrix.compiler_cxx }}" >> $GITHUB_ENV echo "FC=${{ matrix.compiler_fc }}" >> $GITHUB_ENV echo "F77=${{ matrix.compiler_fc }}" >> $GITHUB_ENV export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 export HOMEBREW_NO_AUTO_UPDATE=1 export HOMEBREW_NO_INSTALL_CLEANUP=1 export HOMEBREW_NO_SANDBOX=1 export HOMEBREW_NO_SANDBOX_LINUX=1 echo "HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1" >> $GITHUB_ENV echo "HOMEBREW_NO_AUTO_UPDATE=1" >> $GITHUB_ENV echo "HOMEBREW_NO_INSTALL_CLEANUP=1" >> $GITHUB_ENV echo "HOMEBREW_NO_SANDBOX=1" >> $GITHUB_ENV echo "HOMEBREW_NO_SANDBOX_LINUX=1" >> $GITHUB_ENV if [[ "${{ matrix.os }}" =~ macos ]]; then export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 export HOMEBREW_NO_AUTO_UPDATE=1 export HOMEBREW_NO_INSTALL_CLEANUP=1 echo "HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1" >> $GITHUB_ENV echo "HOMEBREW_NO_AUTO_UPDATE=1" >> $GITHUB_ENV echo "HOMEBREW_NO_INSTALL_CLEANUP=1" >> $GITHUB_ENV brew install ninja brew install libomp else sudo apt-get update if [[ "${{ matrix.compiler }}" =~ gnu ]]; then sudo apt-get install -y libomp5 libomp-dev fi sudo apt-get install -y ninja-build fi printenv - name: Free Disk Space (Ubuntu) # Free up disk space for nvhpc uses: jlumbroso/free-disk-space@main if: contains( matrix.compiler, 'nvhpc' ) continue-on-error: true with: # this might remove tools that are actually needed, # if set to "true" but frees about 6 GB tool-cache: false # all of these default to true, but feel free to set to # "false" if necessary for your workflow android: true dotnet: true haskell: true large-packages: false # takes too long docker-images: false # takes too long swap-storage: true - name: Retrieve cached dependencies if: matrix.caching id: deps-restore-cache uses: actions/cache/restore@v5 with: path: ${{ env.DEPS_DIR }} key: deps-${{ matrix.os }}-${{ matrix.compiler }}-${{ env.CACHE_SUFFIX }} - name: Install NVHPC compiler if: contains( matrix.compiler, 'nvhpc' ) shell: bash -eux {0} run: | ${ATLAS_TOOLS}/install-nvhpc.sh --prefix ${DEPS_DIR}/nvhpc --version 26.1 source ${DEPS_DIR}/nvhpc/env.sh echo "${NVHPC_DIR}/compilers/bin" >> $GITHUB_PATH [ -z ${MPI_HOME+x} ] || echo "MPI_HOME=${MPI_HOME}" >> $GITHUB_ENV # Add a -tp=haswell flag, which is needed to avoid nvhpc errors of "Error during math dispatching processing..." # It seems the compiler detects an incompatible target processor on some of the runners, # in particular -tp=znver4 seems incompatible with "AMD EPYC 9V74 80-Core Processor" as shown by /proc/cpuinfo NVHPC_TARGET_FLAG="-tp=haswell" CFLAGS="${CFLAGS:+$CFLAGS }${NVHPC_TARGET_FLAG}" CXXFLAGS="${CXXFLAGS:+$CXXFLAGS }${NVHPC_TARGET_FLAG}" FFLAGS="${FFLAGS:+$FFLAGS }${NVHPC_TARGET_FLAG}" FCFLAGS="${FCFLAGS:+$FCFLAGS }${NVHPC_TARGET_FLAG}" [ -z ${CFLAGS+x} ] || echo "CFLAGS=${CFLAGS}" >> $GITHUB_ENV [ -z ${CXXFLAGS+x} ] || echo "CXXFLAGS=${CXXFLAGS}" >> $GITHUB_ENV [ -z ${FFLAGS+x} ] || echo "FFLAGS=${FFLAGS}" >> $GITHUB_ENV [ -z ${FCFLAGS+x} ] || echo "FCFLAGS=${FCFLAGS}" >> $GITHUB_ENV - name: Install Intel oneAPI compiler if: contains( matrix.compiler, 'intel' ) run: | wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB sudo apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB rm GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB sudo add-apt-repository "deb https://apt.repos.intel.com/oneapi all main" sudo apt-get update if [[ "${{matrix.compiler}}" == "intel-classic-2021.10" ]]; then version=2023.2.0 # latest oneapi version containing classic compiler (2021.10) sudo apt-get install \ intel-oneapi-compiler-fortran-$version \ intel-oneapi-compiler-dpcpp-cpp-and-cpp-classic-$version \ intel-oneapi-mkl-$version source /opt/intel/oneapi/setvars.sh unset I_MPI_ROOT # We don't want to use the MPI from this compiler. It shows problems else #sudo apt-get install intel-oneapi-toolkit # since 2026 but MKL is too new for eckit 2.0.6 version=2025.3 sudo apt-get install \ intel-oneapi-base-toolkit-$version \ intel-oneapi-hpc-toolkit-$version \ intel-oneapi-compiler-fortran-$version source /opt/intel/oneapi/setvars.sh fi printenv >> $GITHUB_ENV [ -z ${I_MPI_ROOT+x} ] || echo "MPI_HOME=${I_MPI_ROOT}" >> $GITHUB_ENV echo "CACHE_SUFFIX=$CC-$($CC -dumpversion)" >> $GITHUB_ENV - name: Install MPI shell: bash -eux {0} run: | FCFLAGS=-fPIC CFLAGS=-fPIC FFLAGS=-fPIC ${ATLAS_TOOLS}/install-mpi.sh --mpi openmpi --prefix ${DEPS_DIR}/openmpi [ -f ${DEPS_DIR}/openmpi/env.sh ] && source ${DEPS_DIR}/openmpi/env.sh [ -z ${MPI_HOME+x} ] || echo "MPI_HOME=${MPI_HOME}" >> $GITHUB_ENV echo "CACHE_SUFFIX=${CACHE_SUFFIX}-mpi_$(mpirun --version | head -1 | awk '{print $4}')" >> $GITHUB_ENV - name: Install FFTW if: "!contains( matrix.compiler, 'intel' )" # Use Intel MKL FFT functions instead of FFTW for intel compilers shell: bash -eux {0} run: | ${ATLAS_TOOLS}/install-fftw.sh --version 3.3.10 --prefix ${DEPS_DIR}/fftw echo "FFTW_ROOT=${DEPS_DIR}/fftw" >> $GITHUB_ENV - name: Install Qhull if: "!contains( matrix.compiler, 'intel-classic' )" shell: bash -eux {0} run: | ${ATLAS_TOOLS}/install-qhull.sh --version 8.1-alpha3 --prefix ${DEPS_DIR}/qhull echo "Qhull_ROOT=${DEPS_DIR}/qhull" >> $GITHUB_ENV - name: Install LZ4 if: "!contains( matrix.compiler, 'nvhpc' )" run: | if [[ "${{ matrix.os }}" =~ macos ]]; then brew install lz4 echo "LZ4_ROOT=$(brew --prefix lz4)" >> $GITHUB_ENV else sudo apt-get install -y liblz4-dev fi - name: Install Eigen if: "!contains( matrix.compiler, 'nvhpc' )" run: | if [[ "${{ matrix.os }}" =~ macos ]]; then brew install eigen@3 echo "Eigen3_ROOT=$(brew --prefix eigen@3)" >> $GITHUB_ENV else sudo apt-get install -y libeigen3-dev fi - name: Save cached dependencies # There seems to be a problem with cached NVHPC dependencies, leading to SIGILL perhaps due to slightly different architectures if: matrix.caching && matrix.build_type == 'Debug' && github.ref_name == github.event.repository.default_branch id: deps-save-cache uses: actions/cache/save@v5 with: path: ${{ env.DEPS_DIR }} key: ${{ steps.deps-restore-cache.outputs.cache-primary-key }} - name: Set Build & Test Environment run: | # Only add --oversubscribe arg for openmpi [ -z ${I_MPI_ROOT+x} ] && echo "ATLAS_RUN_MPI_ARGS=--oversubscribe" >> $GITHUB_ENV # Stop Intel MPI from attempting an unsupported backend if [ -n "${I_MPI_ROOT+x}" ]; then echo "I_MPI_FABRICS=tcp" >> $GITHUB_ENV echo "FI_PROVIDER=tcp" >> $GITHUB_ENV fi # Add mpirun to path for testing [ -z ${MPI_HOME+x} ] || echo "${MPI_HOME}/bin" >> $GITHUB_PATH # Force UCX (and thus OpenMPI's UCX PML) onto TCP + shared-memory transports. # The GitHub-hosted Azure runners intermittently expose a MANA (mana_0) RDMA/ # InfiniBand device on which UCX fails to create UD queue-pairs, causing # MPI_Init to abort ("failed to create UD QP ... Operation not supported"). # These are single-node tests that never need RDMA, so restrict transports to # loopback TCP + shared memory to make MPI startup deterministic. echo "UCX_TLS=tcp,self,sm" >> $GITHUB_ENV echo "UCX_NET_DEVICES=lo" >> $GITHUB_ENV echo "UCX_LOG_LEVEL=error" >> $GITHUB_ENV - name: Build & Test id: build-test uses: ecmwf/build-package@v2 with: self_coverage: ${{ matrix.coverage }} force_build: true cache_suffix: "${{ matrix.build_type }}-${{ env.CACHE_SUFFIX }}" recreate_cache: ${{ matrix.caching == false }} dependencies: | ecmwf/ecbuild ecmwf/eckit ecmwf/fckit dependency_branch: develop dependency_cmake_options: | ecmwf/eckit: "-G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DENABLE_TESTS=OFF -DENABLE_ECKIT_CMD=OFF -DENABLE_ECKIT_SQL=OFF -DENABLE_WARNINGS=OFF" ecmwf/fckit: "-G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DENABLE_TESTS=OFF" cmake_options: "-G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DATLAS_BITS_LOCAL=64 -DENABLE_WARNING_AS_ERROR=ON -DENABLE_PMR=ON ${{ matrix.cmake_options }}" ctest_options: "${{ matrix.ctest_options }}" - name: Verify tools run: | export PATH=${{ steps.build-test.outputs.bin_path }}:$PATH echo "+ atlas --info" atlas --info echo "+ atlas-grids --list" atlas-grids --list - name: Codecov Upload if: steps.build-test.outputs.coverage_file uses: codecov/codecov-action@v7 with: files: ${{ steps.build-test.outputs.coverage_file }} ecmwf-atlas-b0f79ee/.github/workflows/sync.yml0000664000175000017500000000115015263665111021703 0ustar alastairalastairname: sync # Controls when the workflow will run on: # Trigger the workflow on all pushes push: branches: - '**' tags: - '**' # Trigger the workflow when a branch or tag is deleted delete: ~ jobs: # Calls a reusable CI workflow to sync the current with a remote repository. # It will correctly handle addition of any new and removal of existing Git objects. sync: name: sync uses: ecmwf/reusable-workflows/.github/workflows/sync.yml@v2 secrets: target_repository: atlas/atlas target_username: ClonedDuck target_token: ${{ secrets.BITBUCKET_PAT }} ecmwf-atlas-b0f79ee/.github/workflows/cd.yml0000664000175000017500000000036215263665111021321 0ustar alastairalastairname: cd on: push: tags: - '**' jobs: deploy: uses: ecmwf/reusable-workflows/.github/workflows/create-package.yml@v2 secrets: inherit wheel: uses: ./.github/workflows/build-wheel-wrapper.yml secrets: inherit ecmwf-atlas-b0f79ee/.github/workflows/static-analyzer.yml0000664000175000017500000001264715263665111024056 0ustar alastairalastairname: static-analyzer on: pull_request: types: [opened, synchronize, reopened, closed] jobs: build: if: ${{ github.event_name == 'pull_request' && github.event.action != 'closed' || github.event_name == 'push'}} runs-on: ubuntu-latest outputs: artifact-id: ${{ steps.upload-sareport.outputs.artifact-id }} steps: - name: Checkout atlas uses: actions/checkout@v4 with: path: atlas - name: Checkout ecbuild uses: actions/checkout@v4 with: repository: ecmwf/ecbuild path: ecbuild ref: 'develop' - name: Checkout eckit uses: actions/checkout@v4 with: repository: ecmwf/eckit path: eckit ref: 'develop' - name: Checkout fckit uses: actions/checkout@v4 with: repository: ecmwf/fckit path: fckit ref: 'develop' - name: Install dependencies run: | python -m pip install CodeChecker - name: Generate CMakeLists.txt shell: bash # default on ubuntu-latest run: | # Write the file atomically; fail if anything goes wrong cat > CMakeLists.txt <<'CML' cmake_minimum_required(VERSION 3.18 FATAL_ERROR) #################################################################### macro(ecbundle_add_project package_name) # # add_subdirectory depending on BUILD_${package_name} # set(BUILD_${package_name} ON CACHE BOOL "") if(BUILD_${package_name}) set(dir ${ARGV1}) if(NOT dir) set(dir ${package_name}) endif() add_subdirectory(${dir}) endif() endmacro() macro(ecbundle_set key value) set(${key} ${value} CACHE STRING "") if("${${key}}" STREQUAL "${value}") message(" - ${key} = ${value}") else() message(" - ${key} = ${${key}} [default=${value}]") endif() endmacro() #################################################################### message(" - source : ${CMAKE_CURRENT_SOURCE_DIR}") message(" - build : ${CMAKE_CURRENT_BINARY_DIR}") message(" - install : ${CMAKE_INSTALL_PREFIX}") message(" - build type : ${CMAKE_BUILD_TYPE}") message("Bundle variables set for this build:") ecbundle_set(CMAKE_EXPORT_COMPILE_COMMANDS ON) ecbundle_set(ENABLE_ECKIT_CMD OFF) ecbundle_set(ENABLE_ECKIT_SQL OFF) ecbundle_set(ENABLE_ECKIT_GEO OFF) ecbundle_set(ENABLE_OMP OFF) ecbundle_set(ECKIT_ENABLE_TESTS OFF) ecbundle_set(FCKIT_ENABLE_TESTS OFF) message("") #################################################################### find_package(ecbuild 3.8 REQUIRED HINTS ${CMAKE_CURRENT_SOURCE_DIR}/ecbuild) project(bundle VERSION 0.0.0) ## Initialize include(${CMAKE_CURRENT_BINARY_DIR}/init.cmake OPTIONAL) ## Projects ecbundle_add_project(ecbuild) ecbundle_add_project(eckit) ecbundle_add_project(fckit) ecbundle_add_project(atlas) ## Finalize include(${CMAKE_CURRENT_BINARY_DIR}/final.cmake OPTIONAL) ecbuild_install_project(NAME ${PROJECT_NAME}) ecbuild_print_summary() CML - name: Build sources run: | mkdir build cd build cmake -GNinja .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ninja - name: Analyze sources run: | jq '[ .[] | select(.file | test(".*/atlas/src/.*") ) ]' build/compile_commands.json > atlas_compile_commands.json jq '[ .[] | select(.file | test(".*/pluto/src/.*") ) ]' build/compile_commands.json > pluto_compile_commands.json jq -n --slurpfile atlas atlas_compile_commands.json --slurpfile pluto pluto_compile_commands.json '$atlas[] + $pluto[]' \ > build/filtered_compile_commands.json CodeChecker analyze build/filtered_compile_commands.json -o cc_out --analyzers clangsa -j 4 \ -i atlas/.github/static-analyzer-skip.txt CodeChecker parse -e html cc_out -o ./reports_html --trim-path-prefix ${PWD} || true - name: Archive static-analyzer report id: upload-sareport uses: actions/upload-artifact@v4 with: name: sareport path: reports_html preview-publish: if: ${{ github.event_name == 'pull_request' && github.event.action != 'closed' }} needs: build uses: ecmwf/reusable-workflows/.github/workflows/pr-preview-publish.yml@main with: artifact-id: ${{ needs.build.outputs.artifact-id }} space: docs name: atlas path: static-analyzer link-text: 💣💥☠� Static Analyzer Report ☠�💥💣 link-tag: STATIC-ANALYSIS secrets: sites-token: ${{ secrets.ECMWF_SITES_TOKEN }} preview-unpublish: if: ${{ github.event_name == 'pull_request' && github.event.action == 'closed' }} uses: ecmwf/reusable-workflows/.github/workflows/pr-preview-unpublish.yml@main with: space: docs name: atlas path: static-analyzer secrets: sites-token: ${{ secrets.ECMWF_SITES_TOKEN }} ecmwf-atlas-b0f79ee/.github/docs-config.yml0000664000175000017500000000127115263665111021071 0ustar alastairalastair# note: each step is executed in own process build-steps: - git clone --depth 1 https://github.com/ecmwf/atlas-docs.git $RUNNER_TEMP/atlas-docs - | wget https://github.com/doxygen/doxygen/archive/refs/tags/Release_1_13_2.tar.gz tar xzf Release_1_13_2.tar.gz cmake -S doxygen-Release_1_13_2 -B doxygen cmake --build doxygen -j16 sudo cmake --install doxygen - sudo apt install -y -q ghostscript texlive-full - | cd $RUNNER_TEMP/atlas-docs make PUBLIC=1 WITH_ECKIT=1 WITH_DOXYGEN=1 ATLAS_SOURCE_DIR=$GITHUB_WORKSPACE clean html echo "DOC_BUILD_PATH=$RUNNER_TEMP/atlas-docs/build/html" >> "$GITHUB_ENV" hosts: ecmwf-sites: space: docs name: atlas ecmwf-atlas-b0f79ee/hic/0000775000175000017500000000000015263665111015355 5ustar alastairalastairecmwf-atlas-b0f79ee/hic/tests/0000775000175000017500000000000015263665111016517 5ustar alastairalastairecmwf-atlas-b0f79ee/hic/tests/CMakeLists.txt0000664000175000017500000000137515263665111021265 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) ecmwf-atlas-b0f79ee/hic/tests/test_hic.cc0000664000175000017500000000360615263665111020635 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; } ecmwf-atlas-b0f79ee/hic/tests/test_hicsparse.cc0000664000175000017500000002371615263665111022057 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; } ecmwf-atlas-b0f79ee/hic/tests/test_find_hic/0000775000175000017500000000000015263665111021321 5ustar alastairalastairecmwf-atlas-b0f79ee/hic/tests/test_find_hic/CMakeLists.txt0000664000175000017500000000202015263665111024053 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() ecmwf-atlas-b0f79ee/hic/tests/test_find_hic/find_hic/0000775000175000017500000000000015263665111023064 5ustar alastairalastairecmwf-atlas-b0f79ee/hic/tests/test_find_hic/find_hic/CMakeLists.txt0000664000175000017500000000016515263665111025626 0ustar alastairalastair cmake_minimum_required( VERSION 3.18 FATAL_ERROR ) project( find_hic LANGUAGES CXX ) find_package( hic REQUIRED ) ecmwf-atlas-b0f79ee/hic/tests/test_find_hic/test.sh.in0000775000175000017500000000154515263665111023251 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 "+++++++++++++++++" ecmwf-atlas-b0f79ee/hic/tests/test_hic_dummy.cc0000664000175000017500000000251115263665111022042 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; } ecmwf-atlas-b0f79ee/hic/CMakeLists.txt0000664000175000017500000000563515263665111020126 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() ecmwf-atlas-b0f79ee/hic/LICENSE0000664000175000017500000002500315263665111016362 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. ecmwf-atlas-b0f79ee/hic/AUTHORS0000664000175000017500000000014215263665111016422 0ustar alastairalastairAuthors ======= - Willem Deconinck Thanks for contributions from ============================= ecmwf-atlas-b0f79ee/hic/CHANGELOG.md0000664000175000017500000000000015263665111017154 0ustar alastairalastairecmwf-atlas-b0f79ee/hic/src/0000775000175000017500000000000015263665111016144 5ustar alastairalastairecmwf-atlas-b0f79ee/hic/src/hic/0000775000175000017500000000000015263665111016707 5ustar alastairalastairecmwf-atlas-b0f79ee/hic/src/hic/hic.h0000664000175000017500000000267015263665111017630 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()) ecmwf-atlas-b0f79ee/hic/src/hic/hic_namespace_macro.h0000664000175000017500000000114615263665111023022 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 } #endifecmwf-atlas-b0f79ee/hic/src/hic/hic_library_types.h0000664000175000017500000000302515263665111022573 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 //------------------------------------------------ecmwf-atlas-b0f79ee/hic/src/hic/hicsparse.h0000664000175000017500000001533315263665111021046 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 ecmwf-atlas-b0f79ee/hic/src/hic/hic_dummy/0000775000175000017500000000000015263665111020665 5ustar alastairalastairecmwf-atlas-b0f79ee/hic/src/hic/hic_dummy/dummyShouldNotBeCalled.h0000664000175000017500000000121015263665111025377 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"); } } // namespaceecmwf-atlas-b0f79ee/hic/src/hic/hic_dummy/hicsparse_dummy.h0000664000175000017500000000530315263665111024233 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_CALLEDecmwf-atlas-b0f79ee/hic/src/hic/hic_dummy/hic_dummy_runtime.h0000664000175000017500000000514515263665111024564 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" #include #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}; }; struct dummyDeviceProp_t { char name[256]{"dummy"}; std::size_t totalGlobalMem{0}; int multiProcessorCount{1}; int warpSize{1}; int clockRate{0}; }; DUMMY_FUNCTION(DeviceSynchronize) DUMMY_FUNCTION(Free) DUMMY_FUNCTION(FreeAsync) DUMMY_FUNCTION(GetDevice) DUMMY_FUNCTION(GetDeviceCount) DUMMY_FUNCTION(GetDeviceProperties) 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_CALLEDecmwf-atlas-b0f79ee/hic/src/hic/hic_runtime.h0000664000175000017500000001431315263665111021370 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_CUDA using HIC_SYMBOL(DeviceProp_t) = cudaDeviceProp; #elif HIC_BACKEND_HIP using HIC_SYMBOL(DeviceProp_t) = hipDeviceProp_t; #elif HIC_BACKEND_DUMMY using HIC_SYMBOL(DeviceProp_t) = dummyDeviceProp_t; #endif #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(GetDevice) HIC_FUNCTION(GetDeviceCount) HIC_FUNCTION(GetDeviceProperties) 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 ecmwf-atlas-b0f79ee/hic/src/hic/hic_config.h.in0000664000175000017500000000217215263665111021557 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 ecmwf-atlas-b0f79ee/hic/src/CMakeLists.txt0000664000175000017500000000417615263665111020714 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() ecmwf-atlas-b0f79ee/hic/README.md0000664000175000017500000000005615263665111016635 0ustar alastairalastairhic === HIC: An abstraction to HIP and CUDA ecmwf-atlas-b0f79ee/hic/cmake/0000775000175000017500000000000015263665111016435 5ustar alastairalastairecmwf-atlas-b0f79ee/hic/cmake/hic_fetchcontent_ecbuild.cmake0000664000175000017500000000066215263665111024441 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} ) ecmwf-atlas-b0f79ee/hic/cmake/hic-import.cmake.in0000664000175000017500000000035115263665111022116 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() ecmwf-atlas-b0f79ee/CMakeLists.txt0000664000175000017500000001004315263665111017350 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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 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 LANGUAGES CXX ) set( ATLAS_BITS_GLOBAL 64 CACHE STRING "bits used to define a global index (atlas::gidx_t)" ) set( ATLAS_BITS_LOCAL 32 CACHE STRING "bits used to define a local index (atlas::idx_t)" ) ################################################################################ # Required packages if( ATLAS_BITS_LOCAL MATCHES 64 ) ecbuild_find_package( NAME eckit VERSION 1.17.1 REQUIRED ) else() ecbuild_find_package( NAME eckit VERSION 1.16 REQUIRED ) endif() ecbuild_debug( " eckit_FEATURES : [${eckit_FEATURES}]" ) add_subdirectory(atlas_io) find_package(atlas_io) ################################################################################ # Features that can be enabled / disabled with -DENABLE_ ecbuild_add_option( FEATURE ATLAS_GRID DESCRIPTION "Build grid related features" ) ecbuild_add_option( FEATURE ATLAS_FIELD DESCRIPTION "Build field and memory management related features" ) ecbuild_add_option( FEATURE ATLAS_FUNCTIONSPACE DESCRIPTION "Build functionspace related features (mesh, functionspace, parallel)" CONDITION atlas_HAVE_ATLAS_GRID AND atlas_HAVE_ATLAS_FIELD ) ecbuild_add_option( FEATURE ATLAS_INTERPOLATION DESCRIPTION "Build interpolation related features" CONDITION atlas_HAVE_ATLAS_FUNCTIONSPACE ) ecbuild_add_option( FEATURE ATLAS_TRANS DESCRIPTION "Build transform related features" CONDITION atlas_HAVE_ATLAS_FUNCTIONSPACE ) ecbuild_add_option( FEATURE ATLAS_NUMERICS DESCRIPTION "Build numerics related features" CONDITION atlas_HAVE_ATLAS_FUNCTIONSPACE ) 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 ) ecbuild_add_option( FEATURE FCKIT_DEVELOP DESCRIPTION "Used to enable new features or API depending on eckit develop branch, not yet in a tagged release" DEFAULT OFF ) if (DEFINED ATLAS_ENABLE_CUDA AND NOT DEFINED HIC_ENABLE_CUDA ) set( HIC_ENABLE_CUDA ${ATLAS_ENABLE_CUDA} ) endif() if (DEFINED ATLAS_ENABLE_HIP AND NOT DEFINED HIC_ENABLE_HIP ) set( HIC_ENABLE_HIP ${ATLAS_ENABLE_HIP} ) endif() add_subdirectory( hic ) find_package(hic REQUIRED) add_subdirectory( pluto ) find_package(pluto REQUIRED) include( features/BOUNDSCHECKING ) include( features/FORTRAN ) include( features/CUDA ) include( features/MPI ) include( features/OMP ) include( features/POCKETFFT ) include( features/FFTW ) include( features/ECTRANS ) include( features/TESSELATION ) include( features/GRIDTOOLS_STORAGE ) include( features/ACC ) include( features/EIGEN ) include( features/PROJ ) include( features/SANDBOX ) include( features/CLANG_TIDY ) include( features/INCLUDE_WHAT_YOU_USE ) include( features/INIT_SNAN ) include( features/DOCS ) include( features/ATLAS_RUN ) ################################################################################ # sources include( atlas_compile_flags ) add_subdirectory( src ) add_subdirectory( doc ) ################################################################################ # Export and summarize include( atlas_export ) ecbuild_print_summary() ecmwf-atlas-b0f79ee/LICENSE0000664000175000017500000002500315263665111015617 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. ecmwf-atlas-b0f79ee/AUTHORS0000664000175000017500000000125515263665111015665 0ustar alastairalastairAuthors ======= - Willem Deconinck - Pedro Maciel - Tiago Quintino Thanks for contributions from ============================= - Carlos Osuna - Avi Bahra - Andreas Mueller - Baudouin Raoult - Florian Rathgeber - Oliver Lomax - Marek Wlasak - Daniel Holdaway - Daan Degrauwe - Philippe Marguinaud - Slavko Brdar - Gianmarco Mengaldo - Dušan Figala - Benjamin Menetrier - James Hawkes - Mats Hamrud - Rahul Mahajan - Toby Searle - Olivier Iffrig - Christian Kuehnlein - Iain Russell - Marco Milan - Daniel Tipping - Domokos Sármány - Lorenzo Milazzo - Francois Hebert - Yannick Trémolet - Mark J. Olah - Sam Hatfield - Peter Bispham - Paul Cresswell - Steven Vahl - Michael Lange ecmwf-atlas-b0f79ee/CHANGELOG.md0000664000175000017500000011355315263665111016433 0ustar alastairalastairChangelog ========= All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] ## [0.47.0] - 2026-10-14 ### Added - Add Rust bindings through the `atlas-sys` crate and prepare it for crates.io publishing with registry dependencies, license files, and an automated publishing workflow (#360, #441). - Add `FunctionSpace::mask()`, `setMask()`, and `hasMask()` (#416). - Add Gmsh output policies for missing and masked values (#418). - Add grid construction from JSON strings and support named grids through the `grid` configuration key (#424). - Add `Grid::masterIndex()` and `Grid::flags()` for grids with intrinsic halos and folds, such as ORCA (#428). - Add a FieldBuilder interface with a grid-box-area field builder (#396). - Implement `Trans::invtrans_vordiv2uv` for fields with and without levels (#408). - Add global-index-based edge-to-cell connectivity (#413). - Add Fortran grid type queries, unstructured-grid constructors from coordinates, and mesh constructors from grids, distributions, and partitioners (#398, #404). - Add Fortran FieldSet shape accessors, FunctionSpace checksum array support, and BlockStructuredColumns and FunctionSpace accessors (#376, #389, #391). - Add `ArrayView::extent()` and `LocalView::extent()` for mdspan interoperability (#390). - Add specialized mdspan accessors in pluto (#432). - Add device queries and device properties to hic (#431). - Add `Metadata::json()`, version definitions for downstream introspection, and documentation of Atlas environment variables (#373, #358, #362). - Add `ATLAS_DEBUG_SYNC` macros with a timeout for diagnosing deadlocks and improve macOS backtraces with source line numbers (#385, #375). ### Changed - Update Gmsh output to format 4.1 (#430). - Omit inferred domains and default projections from structured-grid specifications. The constructed grid still provides its domain; consumers requiring an explicit serialized domain should use `grid.domain().spec()` (#424). - Implement BlockStructuredColumns checksums and align checksums across StructuredColumns, NodeColumns, CellColumns, and EdgeColumns. Checksum values may differ from previous releases (#371, #384, #388). - Improve Spectral coefficient indexing and iteration, and support a configurable, stable MPI communicator (#399). - Use pluto allocators in GatherScatter and pooled temporary buffers in TransLocal and TransIFS (#369, #409). - Gather only referenced source rows when applying interpolation with missing values (#397). - Allow automatic source-field halo exchanges to be disabled during interpolation (#411). - Allow GatherScatter setup to preserve global indices explicitly (#419). - Preserve field names when creating fields from existing fields (#372). - Update pocketfft and make its symbols unique to Atlas (#357). ### Fixed - Fix Gmsh output deadlocks and allow CellColumns construction on partitions with zero cells (#440). - Initialize pooled buffers used by TransIFS adjoint operations and fix `invtrans_grad_adj` (#425, #439). - Prevent accidental Spectral functionspace construction from a boolean. - Fix PointCloud GatherScatter setup (#429). - Fix GatherScatter for sparse global indexing in EdgeColumns (#387). - Fix device-state handling in halo exchange (#370). - Fix halo exchange inconsistencies in spherical-vector interpolation (#411). - Fix functionspace metadata broadcasts to use the correct MPI communicator (#414). - Fix ambiguity in `mpi::Scope` and debug synchronization macros (#405, #422). - Fix odd-sized pole patches and exceptional pole-patch partitioning in structured mesh generation (#417, #415). - Fix StructuredColumns coordinate computation for regional grids and grids with non-degree units (#396). - Fix NodeColumns statistics to use owned nodes only (#401). - Fix BlockStructuredColumns checksum hexadecimal output (#386). - Fix Fortran construction of unstructured grids by identifier (#400). - Make spherical-harmonic function evaluation thread-safe (#406). - Work around zero-sized local spectra with ectrans versions older than 2.1 (#399). - Fix tracing CallStack handling (#379). - Improve compatibility with eckit versions newer than 2.2.0, Python wheel builds, and builds without tessellation support (#382, #412, #355). ## [0.46.0] - 2026-03-23 ### Added - Set a custom MPI communicator in TransIFS. by @mo-joshuacolclough in https://github.com/ecmwf/atlas/pull/346 ### Fixed - Fix export of hic-targets with ECBUILD_2_COMPAT=ON (#353) - Fix missing return in Fortran pluto%has_registered_resource - Fix bug in the interpolation procedure (spherical vector) by @mo-lormi in https://github.com/ecmwf/atlas/pull/351 - Fix issues with mpi scope (Outermost mpi::Scope does not restore prior communicator #186) ### Changed * Add ability to redistribute from NodeColumns to StructuredColumns by @lewisn-met in https://github.com/ecmwf/atlas/pull/353 * Improvement do atlas::mpi by @wdeconinck in https://github.com/ecmwf/atlas/pull/348 * Demonstrate use of spherical mean value interpolation for vectors by @lewisn-met in https://github.com/ecmwf/atlas/pull/349 ## [0.45.1] - 2026-02-23 ### Added - Polygon implementation for modern (spherical mean value) interpolation. by @lewisn-met in https://github.com/ecmwf/atlas/pull/329 ### Fixed - Compatibility with eckit 2.0.0 - Allow more time for cubed_meshgen test by @DJDavies2 in https://github.com/ecmwf/atlas/pull/344 - Intel version < 19.1 compilation - Installation script for METIS ## [0.45.0] - 2026-01-14 ### Fixed - Fixes and workaround failing atlas_fctest_functionspace when fckit is compiled with -Mnotarget_temps with NVHPC by @wdeconinck in https://github.com/ecmwf/atlas/pull/332 - Pluto fixes and improvements by @wdeconinck in https://github.com/ecmwf/atlas/pull/335 - [pluto] Fix installation of missing header - FunctionSpace::scatter sets local field to dirty halo - Remove unused header eckit/memory/Noncopyable.h - Fix GatherScatter when global index with index 1 in in halo as is the case with ORCA grids ### Changed - Increase support for parallel interpolation from LAM grids by @wdeconinck in https://github.com/ecmwf/atlas/pull/331 - Detect and treat multiple (2+) discontiguity when wrapping arrays w/ atlas::Field by @sbrdar in https://github.com/ecmwf/atlas/pull/327 - Improve UnstructuredGrid::spec() performance by @wdeconinck in https://github.com/ecmwf/atlas/pull/336 - DelaunayMeshGenerator/ExtendNodesGlobal custom extension grid (defaul… by @pmaciel in https://github.com/ecmwf/atlas/pull/337 - Add cell_maximum_diagonal_on_unit_sphere metadata for global StructuredGrid without projections ### Added - Installation scripts for lz4 and metis - Add cell_maximum_diagonal_on_unit_sphere metadata for global StructuredGrid without projections to improve search in FiniteElement interpolation ## [0.44.1] - 2025-11-07 ### Fixed - Fix loop bounds for gcc+OpenMP compiler by @fmahebert in https://github.com/ecmwf/atlas/pull/324 - Fix compilation of atlas_fctest_fvm_nabla with NAG compiler (#326) by @wdeconinck in https://github.com/ecmwf/atlas/pull/330 - Set Eigen version to eigen@3 in CI to prevent 5.0.0 update ## [0.44.0] - 2025-10-06 ### Added - Add Collect communication pattern by @wdeconinck in https://github.com/ecmwf/atlas/pull/301 - Add array::View interoperability with mdspan by @wdeconinck in https://github.com/ecmwf/atlas/pull/304 - Add support for pocketfft by @wdeconinck in https://github.com/ecmwf/atlas/pull/308, https://github.com/ecmwf/atlas/pull/320 - Improvement in spherical-polygon intersection by @sbrdar in https://github.com/ecmwf/atlas/pull/310 - Improvement in the conservative interpolation for general meshes by @sbrdar in https://github.com/ecmwf/atlas/pull/318 - Add Field::syncHost and Field::syncDevice by @wdeconinck in https://github.com/ecmwf/atlas/pull/311 - Add more GPU tracing capabilities for device allocations and host-device data transfers by @wdeconinck in https://github.com/ecmwf/atlas/pull/312 - Improve HaloExchange on_device, checking state flags by @wdeconinck in https://github.com/ecmwf/atlas/pull/313 - Update interpolation to support hicsparse backend by @l90lpa in https://github.com/ecmwf/atlas/pull/275 - Add Locator to find at which partition and at which index a global index is located by @wdeconinck in https://github.com/ecmwf/atlas/pull/317 - Add matrix halo exchange to "binning" interpolation method. by @odlomax in https://github.com/ecmwf/atlas/pull/315 - Introduce atlas-interpolations app by @wdeconinck in https://github.com/ecmwf/atlas/pull/321 - Support building of StructuredColumns with halos for grids with uneven points at poles e.g. N80 grid ### Changed - Move mdspan from atlas to pluto and update mdspan to latest by @wdeconinck in https://github.com/ecmwf/atlas/pull/303 - Update MeshBuilder API by @wdeconinck in https://github.com/ecmwf/atlas/pull/305 ### Fixed - Fix out-of-range in RegionalLinear2D with FORTRAN feature disabled by @wdeconinck in https://github.com/ecmwf/atlas/pull/322 - Fix empty adjoint matrix for SphericalVector for scalar fields by @wdeconinck in https://github.com/ecmwf/atlas/pull/314 - Support CUDA 13.0 by @wdeconinck in https://github.com/ecmwf/atlas/pull/316 - Fix out-of-range in RegionalLinear2D with FORTRAN feature disabled by @wdeconinck in https://github.com/ecmwf/atlas/pull/322 - Allow BlockStructuredColumns to be used for MatchingPartitioner ## [0.43.1] - 2025-07-09 ### Fixed - Pluto installation of pluto_module.mod file - Avoid temporary array creation in Fortran pluto_allocator%allocate() function ## [0.43.0] - 2025-06-25 ### Added - Python wheel by @tmi in https://github.com/ecmwf/atlas/pull/274, https://github.com/ecmwf/atlas/pull/280 - Add Grid getter to FunctionSpace by @tom-j-h in https://github.com/ecmwf/atlas/pull/264 - Fortran: Export atlas_functionspace_BlockStructuredColumns by @wdeconinck in https://github.com/ecmwf/atlas/pull/276 - Feature/spectral updates by @wdeconinck in https://github.com/ecmwf/atlas/pull/285 - Add Field::halo() to manage and describe field halo's by @wdeconinck in https://github.com/ecmwf/atlas/pull/291 - Add a vector_component option utility by @odlomax in https://github.com/ecmwf/atlas/pull/294 - Implement floating-point-exception trapping for macos by @wdeconinck in https://github.com/ecmwf/atlas/pull/295 ### Fixed - Bugfix/device strides on cpu by @sbrdar in https://github.com/ecmwf/atlas/pull/297 - Support nvidia 24.5 by @wdeconinck in https://github.com/ecmwf/atlas/pull/278 - Set device_updated=false when calling Array::deallocateDevice() (fixes #243) by @wdeconinck in https://github.com/ecmwf/atlas/pull/277 - Fix StructuredGrid periodicity for certain domains (fixes #282) by @wdeconinck in https://github.com/ecmwf/atlas/pull/283 ### Changed - Make FiniteElement Interpolation weight computation multithreaded using OpenMP by @wdeconinck in https://github.com/ecmwf/atlas/pull/292 - Added OpenMP to the cubed-sphere interpolator and the matching cubed sphere partitioner. by @odlomax in https://github.com/ecmwf/atlas/pull/293 - Deprecate aliases for structured interpolation methods by @wdeconinck in https://github.com/ecmwf/atlas/pull/296 ## [0.42.0] - 2025-04-09 ### Added - Cubed sphere 2 grid builder by @mo-jonasganderton in https://github.com/ecmwf/atlas/pull/253 - Grid/Distribution constructor for PointCloud by @tom-j-h in https://github.com/ecmwf/atlas/pull/262 - Add distribution of serial interpolation matrix by @sbrdar in https://github.com/ecmwf/atlas/pull/258 - Add offloading of discontiguous arrays in https://github.com/ecmwf/atlas/pull/267 - Add device offload operators on FieldSet in https://github.com/ecmwf/atlas/pull/268 - Introducing Pluto by @wdeconinck in https://github.com/ecmwf/atlas/pull/269 - Python wheel: initial setup by @tmi in https://github.com/ecmwf/atlas/pull/257 ### Fixed - Fix inconsistent index-base used in StructuredColumns::remote_index by @sbrdar in https://github.com/ecmwf/atlas/pull/265 - Fix OpenMP related bug in DistributionArray ### Changed - Use pluto where possible instead of hic calls by @wdeconinck in https://github.com/ecmwf/atlas/pull/270 - Use pluto default memory resources in atlas::array by @wdeconinck in https://github.com/ecmwf/atlas/pull/273 ## [0.41.1] - 2025-02-18 ### Fixed - Add CI with gpu - Add -Werror in CI to avoid new warnings in the future - Fix compilation with ATLAS_BITS_LOCAL=64 and add it to CI to avoid errors in the future ## [0.41.0] - 2025-02-10 ### Added - Add hicsparse as wrapper to cusparse and hipsparse (#237) - Add SparseMatrix multiply_add functionality (#240) - Replace eckit::SparseMatrix with SparseMatrixStorage and SparseMatrixView to support host/device memory spaces (#247) - Add hicSparse backend to sparse matrix multiply (#246) - New simplified cubed sphere grid (#245) - Add functionality to gather global (serial) sparse matrix from an interpolation (#255) ### Fixed - Fix warnings (#256) - bugfix: DIV_BY_ZERO in MatchingFunctionSpacePartitionerLonLatPolygon for small grids with OMP (#244) - bugfix: Apply normalisation also to SphereT as was already done for unit-sphere (#242) ## [0.40.0] - 2024-11-18 ### Added - Add MultiField to pack field allocations by @sbrdar in https://github.com/ecmwf/atlas/pull/229 - Add function to create `std::variant` for multiple array views. by @odlomax in https://github.com/ecmwf/atlas/pull/220 - Add Fortran access to device data through Field and FieldSet by @sbrdar in https://github.com/ecmwf/atlas/pull/232 - Add Fortran API for structured stencil computations by @wdeconinck in https://github.com/ecmwf/atlas/pull/228 ### Changed - Support OpenACC with Cray compiler + use OpenACC_Fortran_FLAGS by @wdeconinck in https://github.com/ecmwf/atlas/pull/225 - Refactor `SphericalVector` interpolation method to use `array::ArrayViewVariant`. by @odlomax in https://github.com/ecmwf/atlas/pull/227 - Integrate `pack_vector_fields` into `SphericalVector` Interpolation method. by @odlomax in https://github.com/ecmwf/atlas/pull/224 - Refactor `util::pack_vector_fields` to use `array::ArrayViewVariant` by @odlomax in https://github.com/ecmwf/atlas/pull/226 ### Fixed - Fix application of limiter for StructuredInterpolation2D by @wdeconinck in https://github.com/ecmwf/atlas/pull/236 - Fix use of ATLAS_ENABLE_CUDA ON/OFF - Bugfix for Qhull by @benjaminmenetrier in https://github.com/ecmwf/atlas/pull/230 - Fixed ordering of `fixup_halos` and `halo_exhange.execute_adjoint` in `StructuredColumns.cc` by @odlomax in https://github.com/ecmwf/atlas/pull/223 ## [0.39.0] - 2024-09-18 ### Added - Add HIC abstraction layer for HIP and CUDA by @wdeconinck in https://github.com/ecmwf/atlas/pull/219 - Support for HIP via HIC - Add regional interpolation by @benjaminmenetrier in https://github.com/ecmwf/atlas/pull/215 - Pack vector components into higher-rank vector fields. by @odlomax in https://github.com/ecmwf/atlas/pull/218 ### Fixed - Fix missing header in FieldImpl.h by @tehrengruber in https://github.com/ecmwf/atlas/pull/214 - Bug fixes to vector interpolation with StructuredColumns and spherical vector interpolation by @MarekWlasak in https://github.com/ecmwf/atlas/pull/222 ## [0.38.1] - 2024-07-15 ### Fixed - Compilation and running with NAG compiler - Wrap out-of-bounds source cells into the domain (#211) ## [0.38.0] - 2024-06-20 ### Added - Make non_linear interpolation independent of a chosen value type by @wdeconinck in https://github.com/ecmwf/atlas/pull/176 - Procedure to carry out a regridding from high to low resolution (binning) by @mo-lormi in https://github.com/ecmwf/atlas/pull/191 - Add Fortran interface for node-to-edge connectivity building by @benjaminmenetrier in https://github.com/ecmwf/atlas/pull/209 - CUDA/OpenACC capable fields with Native storage backend @sbrdar and @wdeconinck in https://github.com/ecmwf/atlas/pull/182 ### Changed - Make non_linear interpolation independent of a chosen value type by @wdeconinck in https://github.com/ecmwf/atlas/pull/176 ### Fixed - Remove float in Triag2D intersection algorithm by @fmahebert in https://github.com/ecmwf/atlas/pull/203 - Allow zero-sized interpolation target functionspace by @odlomax in https://github.com/ecmwf/atlas/pull/206 - Made sure cubed-sphere interpolation method always sets metadata. by @odlomax in https://github.com/ecmwf/atlas/pull/208 - Avoid silent errors accessing Fieldset fields by ambiguous names by @wdeconinck in https://github.com/ecmwf/atlas/pull/210 - Fixes opposite pole coordinates by @benjaminmenetrier in https://github.com/ecmwf/atlas/pull/202 ## [0.37.0] - 2024-04-09 ### Added - Add SphericalVector interpolation method using parallel transport (#163) - Added arrayForEachDim method ### Fixed - Bugfix for regional grids with ny > nx - Fix build for configuration setting ATLAS_BITS_LOCAL=64 ### Changed - Use new LocalConfiguration baseclass functions in util::Config and util::Metadata instead of eckit::Value backdoor - atlas_io is an adaptor library when eckit_codec is available (#181) ## [0.36.0] - 2023-12-11 ### Added - Add TriangularMeshBuilder with Fortran API, so far for serial meshes only - Add Fortran API for CellCollumns functionspace - Add EqualAreaPartitioner which is geometry based rather than loadbalanced like EqualRegionsPartitioner ### Fixed - Compilation with Intel LLVM compiler - Fix 180 degrees phase shift error in MDPI_gulfstream function ### Changed - Update install scripts - Preparation for using eckit::codec as backend for atlas_io ## [0.35.1] - 2023-10-24 ### Added - Don't output with output::Gmsh the triangle elements with wrong orientation when coordinates are "lonlat" - Add control to skip Gmsh-output of triangles with too large edge length ratio - Configurable geometry in KDTree - Use configurable KDTree geometry in PointCloud - atlas-grid-points executable to list grid points - Allow constructor of atlas::io::ArrayShape with different integer types - Support atlas_io with vector ### Fixed - Control FPE in StructuredColumns::checksum to avoid overflow and invalid in unimportant halo regions - Fixes to MeshBuilder validate_mesh_vs_grid ### Changed - Use search radius in FiniteElement interpolation when mesh defines metadata to do so ## [0.35.0] - 2023-02-10 ### Added - Add accessors for the GridBox class (MIR-632) - Add FunctionSpace::partition() field - Add configurable MPI communicator to data structures (#131) - Add single precision support for fvm::Nabla - Implement PointCloud parallel construction with halo_radius ### Fixed - Fix StructuredMeshGenerator for Gaussian grids that don't start at 0 degrees (ATLAS-375) - Remove functionspace::Spectral via Trans initialisation (fixes #153) - Enable MeshBuilder to set up the Grid (#152) - Fix use of cmake option ATLAS_ENABLE_TRANS as in a bundle ### Changed - Cleanup long-standing temporary element types. API change but with deprecated old API - Deprecated rename Mesh::nb_partitions -> Mesh::nb_parts - Implement Delaunay triangulation using Qhull instead of CGAL but CGAL can still be enabled instead for now ## [0.34.0] - 2023-07-10 ### Added - Fieldset::metadata (#126) - Fieldset::adjointHaloExchange - Field/Fieldset::clone method - Functions to enable/disable FPE - Add function to build mesh from imported connectivity data (#135) - Implement field::for_each capabilities (#139) - Introduce colon-separated environment variable ATLAS_PLUGIN_PATH to simplify plugin detection - Introduce atlas::mdspan, contributed from github.com/kokkos/mdspan - Add function Field::horizontal_dimension() -> std::vector - Setup horizontal_dimensions() for BlockStructuredColumns fields - Upgrade the halo exchange procedure for the function space 'PointCloud' (#120) ### Fixed - Enable latitude normalisation in KDTree coordinate transform (#140) - Fix LocalView indexing bug for non-contiguous slices - C++17 flag public propagation to downstream C++ CMake packages - Fix cells().global_index() metadata for RegularLonLat grids in StructuredMeshGenerator - BuildHalo: mark interior added cells as ghost ## [0.33.0] - 2023-04-03 ### Added - Add support for StructuredPartitionPolygon with halo > 0 - Add information on atlas having PROJ support ### Changed - C++17 standard is now a requirement ### Fixed Fix StructuredInterpolation2D with retry for failed stencils ## [0.32.1] - 2023-02-09 ### Added - Added (lon, lat) to (alpha, beta) transforms to cubed sphere projection ## [0.32.0] - 2023-01-23 ### Added - Added BlockStructuredColumns FunctionSpace - Added function SolidBodyRotation - Added convenience Fortran constructors for ShiftedLonLat, SHiftedLon, ShiftedLat - Support for more FunctionSpaces in various interpolation methods ### Changed - PolygonLocator now wraps around longitudes for non-matching domains - StructuredMeshGenerator can generate meshes with partitions without elements - SerialDistribution makes every MPI task have the entire mesh ### Fixed - Remove assertion for checking of empty mesh in NodeColumns FunctionSpace - Gaussian latitudes can now be computed for very high resolution, without running out of memory. - Interpolation to functionspaces with empty partitions ## [0.31.1] - 2022-11-11 ### Fixed - Fix bug introduced in StructuredMeshGenerator global numbering with release 0.31.0 in commit a63fc62a2 - Fix healpix global numbering for pentagon pole elements in parallel - Fix validity check of atlas::HealpixGrid ## [0.31.0] - 2022-11-10 ### Added - Extend PointCloud functionspace to do halo exchanges, including Fortran API - Add FunctionSpace::gather and FunctionSpace::scatter abstraction ### Changed - Improve performance of MatchingMeshPartitionerLonLatPolygon using OpenMP - Improve performance of BuildHalo using OpenMP and unordered_map - Improve performance of StructuredMeshGenerator using OpenMP - Improve performance of ATLAS_TRACE - Reduce memory peak in GatherScatter setup - Global element numbering of RegularLonLat grids is now following rows independent of partitioning ### Fixed - Running CI with Github Actions - Fix output of atlas-grids y-range for shifted grids - Fix building with ATLAS_BITS_LOCAL=64 ## [0.30.0] - 2022-08-22 ### Added - Fortran API for Interpolation::execute_adjoint() - Fortran API for FieldSet::name() - Fortran API for MeshGenerator::generate(grid,partitioner) - Pentagon element type - SphericalHarmonic function - New interpolation method: ConservativeSphericalPolygonInterpolation - Support 'variables' option in functionspace::PointCloud::createField ### Changed - Atlas-IO is now standalone project, still embedded but only depending on eckit - Deprecate Trans naming of 'ifs' or 'trans' in favour of 'ectrans' - Default StructuredMeshGenerator partitioner is equal_regions instead of trans/ectrans ### Fixed - Fix global numbering HEALPix grid to standard - Fix NodeColumns remote_index for parallel orca grids - Fix use of ATLAS_LINALG_DENSE_BACKEND environment variable ## [0.29.0] - 2022-04-21 ### Added - MatchingMeshPartitioner "cubedsphere" - Interpolator "cubedsphere-bilinear" - Improvements to Interpolation::Cache - Add support for rank 2 fields when a nonlinear action is added to the interpolator - Create Array using ArraySpec only ### Changed - FieldSet::has(...) replaces FieldSet::has_field(...) - Metadata return value to Interpolation::execute() - Rename BilinearRemapping to UnstructuredBilinearLonLat ### Fixed - Compatibility with proj version >= 8 - Compatibility with eckit version <= 1.18.5 - Compatibility with GridTools backend and using 64bit idx_t - Wrongly computed Jacobian::transpose() introduced in 0.28.0 - Fix bug where using ectrans was not enabling adjoint of invtrans - Avoid segfault when OpenMP tasking is broken, as it is with AppleClang and LLVM libomp ## [0.28.1] - 2022-03-14 ### Fixed - Fix compilation for GNU 7.3 ## [0.28.0] - 2022-03-02 ### Added - Assignment of ArrayView from ArrayView - Grid "regional_variable_resolution" via a new VariableResolutionProjection - CubedSphereDualMeshGenerator - VortexRollup function as analytical field for initialising data - ConvexSphericalPolygon utility class - Improve Projection::Jacobian - Initial implementation for bilinear interpolation for unstructured meshes ### Changed - Use new eckit (1.19.0) Sparse and Dense linear algebra API - General robustness improvements to CubedSphere to using functionspaces with various halos ### Fixed - Workarounds to fix compilation with Fujitsu compiler - Workarounds to avoid Cray compiler problems with certain flag combinations - CellColumns::haloExchange for meshes with multiple element types - Computation of HEALPix mesh remote indices. ## [0.27.0] - 2021-12-03 ### Added - Adjoint interpolation with some restrictions - Cubed sphere grid partitioner - Cubed sphere parallel mesh generation - Cubed sphere function spaces - Fortran interfaces to Projection methods - Support discovery of open-source ectrans - Dense linear Algebra matrix_multiply abstraction ### Changed - Remove etc/atlas/config.yaml because defaults should be in code - Naming of sparse_matrix_multiply backend 'omp' -> 'openmp' - Applied clang-format 13.0.0 (all files touched) ## [0.26.0] - 2021-08-23 ### Added - Support for cubed sphere grids and preliminary support for cubes sphere mesh generation. ### Fixed - Compilation with altas_bits_local=64 - Too aggressive optimisation with gnu 11 - Compatibility with cmake 3.20 and nvhpc compilers ## [0.25.0] - 2021-05-18 ### Added - New concept Redistribution to redistribute field on same grid but with different partitioner - Support for Trans::invtrans_adj() and related methods - Introduce ~atlas/etc/atlas/config.yaml - atlas-io with support for encoding/decoding std::array - atlas-io print support for tiny arrays ### Changed - atlas-io version 0.2 - Move util::SpecRegistry to non-templated grid::SpecRegistry ### Fixed - minor bug fixes ## [0.24.1] - 2021-04-06 ### Fixed - Periodic halo for HEALPix mesh - General warnings and suggestions by DeepCode bot - Compilation problems with clang 3.8 ## [0.24.0] - 2021-03-19 ### Fixed - Fixed hang in band distribution for L160000x8000 grid - Fixes to Spectral functionspace and TransIFS regarding vertical levels ### Changed - Requires eckit 1.16 ### Added - atlas-io first version, not for operational use - Spec registry for grids ## [0.23.0] - 2021-01-19 ### Fixed - Structured interpolation method interpolating to area straddling Greenwich. - Fixes when compiling with ATLAS_BITS_LOCAL=64 ### Changed - Possibility to link to alternative open-source version of IFS trans library. ### Added - Caching mechanism for interpolation ## [0.22.1] - 2020-10-22 ### Fixed - Installation of PGI compilers via tools/install-pgi.sh - Allow dependency on older Eigen 3.2 which does not use CMake targets ## [0.22.0] - 2020-10-14 ### Fixed - Feature INIT_SNAN was not working - Support KNearestNeighbour interpolation for functionspace with smaller halo than the mesh contains - Support array size up to size_t limit ### Changed - Migration to use ecbuild 3.4 - ATLAS_BITS_LOCAL can be configured to 32 or 64 ### Added - Fields can be created with given alignment, which adds padding in innermost dimension - Added conservative interpolation with "grid-box average" and "grid-box maximum" - Missing value definition for fields - Add support for missing values in matrix-based interpolation methods - Floating point trapping and signal handling mechanism - Fortran: GridDistribution constructors - Fortran: Domain access - Fortran: Get lonlat_bounding_box via domain - Possibility to access Jacobian of projections (with only some projections implemented) ## [0.21.0] - 2020-06-23 ### Fixed - Fixed Rotation order of applying the rotation angle - Fix nabla computation of surface vector fields - Fix registration and destruction issues of halo-exchange caches - Workaround Clang compiler problem with OpenMP tasking, using introspection - Fix bug in conversion of negative degrees to microdegrees - Fix problem in distribution of work amongst OpenMP threads in StructuredColumns::setup - Fix problem with StructuredColumns creation for grids defined with domains with negative West - Fix computation of Grid::lonlatBoundingBox for arbitrary projections crossing the dateline. ### Changed - Snap LinearSpacing values to start and endpoint to allow exact comparisons - Improved performance and memory requirement of cropping of large StructuredGrids - Regional grids by default now have a positive y-numbering (previously negative). ### Added - PolygonLocator based on functionspace partition polygons. - KDTree class which abstracts eckit concrete implementations, including Fortran interface - Geometry class with factory mechanism to configure which spheroid to use, including Fortran interface - StrcutredGrid function for i,j to index and inverse - Fortran interface to create StructuredColumns with custom distribution - Fortran interface to grid spec - Fortran interface to Projection - Adjoint of HaloExchange - Plugin mechanism to load plugins at runtime. - Fortran constructors for atlas_RegionalGrid - Fortran constructors for projected reduced Gaussian grids - Add copy constructor and assignment operator for atlas::vector - Mercator projection support for scaling, and operation on ellipsoid. - Grid Distribution can now also be created as a function, e.g. for Serial or Bands ## [0.20.2] - 2020-04-27 ### Fixed - Avoid 100ds of compilation warnings introduced in version 0.20.0 ## [0.20.1] - 2020-04-08 ### Fixed - Make feature BOUNDSCHECKING work again. It was not turned on for DEBUG builds - Workaround clang OpenMP bug - Fix Segfault due to unexpected order of destruction of singleton objects ### Added - atlas-grids tool can now be used to compute approximate North-South grid resolution ## [0.20.0] - 2020-03-06 ### Fixed - Pole edges hould not be created for global regular grids with points at poles - Update compatibility with more recent GridTools - HaloExchange with CUDA - Self registration from external library ### Added - Proj-based projections - OpenMP functions for sorting, filling, copying - Parallel structured grid interpolation ### Changed - Grid iterators can have random access - Speed improvements for StructuredColumns constructor - Speed improvements for LonLatPolygon::contains() - Port to ecbuild 3 (also minimum required version) - Tidying up of atlas tools ## [0.19.2] - 2020-01-28 ### Changed - Compatibility with eckit 1.7 due to API change in eckit::LocalConfiguration ## [0.19.1] - 2019-12-19 ### Fixed - Keep Gaussian identity of a Gaussian grid if a given domain does not crop any latitudes - Fix naming for LegendreCache, to be more specific, and platform independent ## [0.19.0] - 2019-10-01 ### Fixed - Lambert ( conformal conic ) projection xy coordinates are now corrected ### Changed - LambertProjection renamed to LambertConformalConic ### Added - Reordering of nodes strategies (Hilbert curve, ReverseCuthillMckee) - Preliminary CellColumns functionspace with Gmsh IO; halos are not yet fully supported ## [0.18.1] - 2019-08-10 ### Fixed - Match vertical structured interpolation to IFS - Fix in creating vertical dimension in StructuredColumns using interval - Fix in caching StructuredColumnsHaloExchange ## [0.18.0] - 2019-07-15 ### Changed - Make grid hashes crossplatform ### Added - Fortran: Can now use TransLocal - Fortran: Can now create unstructured grids - LonLat Bounding box computations for grids using its projection - Serialisation of Mesh Connectivity tables to/from eckit::Stream ### Fixed - Structured interpolation bugs - StructuredColumns bug with iSend - Memory corruption in Spectral functionspace with GT CUDA backend ## [0.17.2] - 2019-06-04 ### Fixed - Compilation with PGI 19.4 - Structured Grid creation for periodic domains that do not start at 0 degrees longitude (Greenwich) ## [0.17.1] - 2019-04-22 ### Added - Option to declaration of field type (vector/scalar) when creating field in FunctionSpace - New projection: Lambert Azimuthal Equal Area - StructuredInterpolation2D to target FunctionSpace StructuredColumns ### Fixed - Compilation with IBM XL 19 - Compilation with Intel 19 ## [0.17.0] - 2019-04-02 ### Changed - OpenMP is now private dependency - Dependencies are now added in a modern CMake3 way - Fortran modules are installed in /module/atlas ### Added - Spectral functionspace better used in distributed context - Nabla now holds shared_ptr to Method - Fortran access to vertical coordinates from StructuredColumns ### Fixed - Compilation with PGI/19.1 (regression) - Add missing halo_exchange for Fortran rank-4 arrays - Memory leaks with StructuredColumns ## [0.16.0] - 2019-02-14 ### Changed - Interpolation makes use of OpenMP - Cleanup of header includes - fypp Fortran preprocessor is ported to fckit 0.6 ### Added - Parallel structured interpolation methods (2D,3D): linear, cubic, quasicubic - Interpolation for multi-level and multi-variable fields - atlas_Trace: Fortran API and use within OpenMP parallel regions - StructuredColumns halo-exchange for vector fields - Field::halo_exchange() function ### Fixed - Fortran compilation with PGI 18.10 - Access to Field view within OpenMP parallel region - FunctionSpaces use only required halo, even if larger halo is available in mesh - Fixed faulty name of a Field when created through Fortran API, wrapping existing memory - Fix NodeColumns functionspace when mesh is created from projected grid. - Parallel interpolation from regular lonlat grid. - Spectral spherical harmonics transforms for large cases ## [0.15.2] - 2018-08-31 ### Changed - Initialisation of Fields to signalling NaN in debug builds, uninitialised in non-debug builds (used to be initialised to zero as part of std::vector construction) ### Added - Implementation of cropped unstructured grids so that spectral transforms to unstructured grids are allowed ### Fixed - Spectral transforms to grids including pole and equator - Build with gridtools CUDA backend ## [0.15.1] - 2018-07-17 ### Fixed - Compilation for Intel 18 debug - Memory bug for spherical harmonics - Compatibility with fckit 0.5.1 ## [0.15.0] - 2018-06-19 ### Changed - Native Array data storage uses now a raw C pointer instead of std::vector - Significant performance improvements to Spherical harmonics transforms ### Fixed - Various bugs related to parallel halos - Bit reproducibility for parallel interpolation ## [0.14.0] - 2018-03-22 ### Added - Spherical Harmonics transforms can receive a cache memory handle ### Changed - Earth interface (C++) - Requires eckit 0.20.0, fckit 0.5.0 ## [0.13.2] - 2018-03-20 ### Fixed - C++ compilation using PGI 17, 18 and GCC 7 - Support Python 3 to generate Fortran bindings - Travis CI linked with github repository - Problem with CUDA allocated memory ## [0.13.1] - 2018-03-01 ### Fixed - Fortran compilation using Intel 18 - GridTools compatibility ## 0.13.0 - 2018-02-16 [Unreleased]: https://github.com/ecmwf/atlas/compare/master...develop [0.46.0]: https://github.com/ecmwf/atlas/compare/0.45.1...0.46.0 [0.45.1]: https://github.com/ecmwf/atlas/compare/0.45.0...0.45.1 [0.45.0]: https://github.com/ecmwf/atlas/compare/0.44.1...0.45.0 [0.44.1]: https://github.com/ecmwf/atlas/compare/0.44.0...0.44.1 [0.44.0]: https://github.com/ecmwf/atlas/compare/0.43.1...0.44.0 [0.43.1]: https://github.com/ecmwf/atlas/compare/0.43.0...0.43.1 [0.43.0]: https://github.com/ecmwf/atlas/compare/0.42.0...0.43.0 [0.42.0]: https://github.com/ecmwf/atlas/compare/0.41.0...0.42.0 [0.41.1]: https://github.com/ecmwf/atlas/compare/0.41.0...0.41.1 [0.41.0]: https://github.com/ecmwf/atlas/compare/0.40.0...0.41.0 [0.40.0]: https://github.com/ecmwf/atlas/compare/0.39.0...0.40.0 [0.39.0]: https://github.com/ecmwf/atlas/compare/0.38.1...0.39.0 [0.38.1]: https://github.com/ecmwf/atlas/compare/0.38.0...0.38.1 [0.38.0]: https://github.com/ecmwf/atlas/compare/0.37.0...0.38.0 [0.37.0]: https://github.com/ecmwf/atlas/compare/0.36.0...0.37.0 [0.36.0]: https://github.com/ecmwf/atlas/compare/0.35.1...0.36.0 [0.35.1]: https://github.com/ecmwf/atlas/compare/0.35.0...0.35.1 [0.35.0]: https://github.com/ecmwf/atlas/compare/0.34.0...0.35.0 [0.34.0]: https://github.com/ecmwf/atlas/compare/0.33.0...0.34.0 [0.33.0]: https://github.com/ecmwf/atlas/compare/0.32.1...0.33.0 [0.32.1]: https://github.com/ecmwf/atlas/compare/0.32.0...0.32.1 [0.32.0]: https://github.com/ecmwf/atlas/compare/0.31.1...0.32.0 [0.31.1]: https://github.com/ecmwf/atlas/compare/0.31.0...0.31.1 [0.31.0]: https://github.com/ecmwf/atlas/compare/0.30.0...0.31.0 [0.30.0]: https://github.com/ecmwf/atlas/compare/0.29.0...0.30.0 [0.29.0]: https://github.com/ecmwf/atlas/compare/0.28.1...0.29.0 [0.28.1]: https://github.com/ecmwf/atlas/compare/0.28.0...0.28.1 [0.28.0]: https://github.com/ecmwf/atlas/compare/0.27.0...0.28.0 [0.27.0]: https://github.com/ecmwf/atlas/compare/0.26.0...0.27.0 [0.26.0]: https://github.com/ecmwf/atlas/compare/0.25.0...0.26.0 [0.25.0]: https://github.com/ecmwf/atlas/compare/0.24.1...0.25.0 [0.24.1]: https://github.com/ecmwf/atlas/compare/0.24.0...0.24.1 [0.24.0]: https://github.com/ecmwf/atlas/compare/0.23.0...0.24.0 [0.23.0]: https://github.com/ecmwf/atlas/compare/0.22.1...0.23.0 [0.22.1]: https://github.com/ecmwf/atlas/compare/0.22.0...0.22.1 [0.22.0]: https://github.com/ecmwf/atlas/compare/0.21.0...0.22.0 [0.21.0]: https://github.com/ecmwf/atlas/compare/0.20.2...0.21.0 [0.20.2]: https://github.com/ecmwf/atlas/compare/0.20.1...0.20.2 [0.20.1]: https://github.com/ecmwf/atlas/compare/0.20.0...0.20.1 [0.20.0]: https://github.com/ecmwf/atlas/compare/0.20.0...0.19.2 [0.19.2]: https://github.com/ecmwf/atlas/compare/0.19.1...0.19.2 [0.19.1]: https://github.com/ecmwf/atlas/compare/0.19.0...0.19.1 [0.19.0]: https://github.com/ecmwf/atlas/compare/0.18.1...0.19.0 [0.18.1]: https://github.com/ecmwf/atlas/compare/0.18.0...0.18.1 [0.18.0]: https://github.com/ecmwf/atlas/compare/0.17.2...0.18.0 [0.17.2]: https://github.com/ecmwf/atlas/compare/0.17.1...0.17.2 [0.17.1]: https://github.com/ecmwf/atlas/compare/0.17.0...0.17.1 [0.17.0]: https://github.com/ecmwf/atlas/compare/0.16.0...0.17.0 [0.16.0]: https://github.com/ecmwf/atlas/compare/0.15.2...0.16.0 [0.15.2]: https://github.com/ecmwf/atlas/compare/0.15.1...0.15.2 [0.15.1]: https://github.com/ecmwf/atlas/compare/0.15.0...0.15.1 [0.15.0]: https://github.com/ecmwf/atlas/compare/0.14.0...0.15.0 [0.14.0]: https://github.com/ecmwf/atlas/compare/0.13.2...0.14.0 [0.13.2]: https://github.com/ecmwf/atlas/compare/0.13.1...0.13.2 [0.13.1]: https://github.com/ecmwf/atlas/compare/0.13.0...0.13.1 ecmwf-atlas-b0f79ee/src/0000775000175000017500000000000015263665111015401 5ustar alastairalastairecmwf-atlas-b0f79ee/src/tests/0000775000175000017500000000000015263665111016543 5ustar alastairalastairecmwf-atlas-b0f79ee/src/tests/linalg/0000775000175000017500000000000015263665111020011 5ustar alastairalastairecmwf-atlas-b0f79ee/src/tests/linalg/test_linalg_sparse_matrix.cc0000664000175000017500000002356115263665111025575 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); } ecmwf-atlas-b0f79ee/src/tests/linalg/CMakeLists.txt0000664000175000017500000000330215263665111022547 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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()ecmwf-atlas-b0f79ee/src/tests/linalg/test_linalg_dense.cc0000664000175000017500000001162515263665111024010 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); } ecmwf-atlas-b0f79ee/src/tests/linalg/test_linalg_sparse_gpu.cc0000664000175000017500000002003615263665111025056 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); }ecmwf-atlas-b0f79ee/src/tests/linalg/test_linalg_sparse.cc0000664000175000017500000003652615263665111024216 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); } ecmwf-atlas-b0f79ee/src/tests/linalg/test_linalg_sparse_matrix_gpu.cc0000664000175000017500000003277715263665111026461 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); } ecmwf-atlas-b0f79ee/src/tests/linalg/helper_linalg_sparse.h0000664000175000017500000000773115263665111024354 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 ecmwf-atlas-b0f79ee/src/tests/linalg/test_linalg_triplet.cc0000664000175000017500000001542215263665111024374 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); } ecmwf-atlas-b0f79ee/src/tests/output/0000775000175000017500000000000015263665111020103 5ustar alastairalastairecmwf-atlas-b0f79ee/src/tests/output/CMakeLists.txt0000664000175000017500000000243615263665111022650 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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_gmsh_empty_partition SOURCES test_gmsh_empty_partition.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_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() ecmwf-atlas-b0f79ee/src/tests/output/test_gmsh_empty_partition.cc0000664000175000017500000000256315263665111025724 0ustar alastairalastair#include #include #include "atlas/grid.h" #include "atlas/mesh/HybridElements.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" namespace atlas { namespace test { void write_mesh_with_empty_partition(bool gather) { EXPECT_EQ(mpi::size(), 2); Grid grid("F4"); std::vector partitions(grid.size(), 0); grid::Distribution distribution(2, grid.size(), partitions.data()); Mesh mesh = StructuredMeshGenerator().generate(grid, distribution); if (mpi::rank() == 0) { EXPECT(mesh.cells().size() > 0); } else { EXPECT_EQ(mesh.cells().size(), 0); EXPECT_EQ(mesh.edges().size(), 0); } const std::string filename = gather ? "test_gmsh_empty_partition_gather.msh" : "test_gmsh_empty_partition.msh"; output::Gmsh(filename, util::Config("gather", gather) | util::Config("info", true)).write(mesh); mpi::comm().barrier(); } CASE("test_gmsh_empty_partition") { write_mesh_with_empty_partition(false); } CASE("test_gmsh_empty_partition_gather") { write_mesh_with_empty_partition(true); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); }ecmwf-atlas-b0f79ee/src/tests/output/fctest_gmsh.F900000664000175000017500000000570715263665111022702 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/output/test_pointcloud_io.cc0000664000175000017500000003623215263665111024326 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/output/test_gmsh_read.cc0000664000175000017500000000225515263665111023406 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/output/test_gmsh.cc0000664000175000017500000006464115263665111022422 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/MakeView.h" #include "atlas/field/Field.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/actions/BuildEdges.h" #include "atlas/output/Gmsh.h" #include "atlas/output/Output.h" #include "atlas/util/Topology.h" #include "atlas/util/Config.h" #include "tests/AtlasTestEnvironment.h" #include "tests/TestMeshes.h" namespace atlas { namespace test { //----------------------------------------------------------------------------- namespace { struct NodeData { int components; std::vector indices; std::vector values; }; NodeData read_scalar_node_data(const std::string& path) { std::ifstream file(path); std::string line; while (std::getline(file, line) && line != "$NodeData") { } EXPECT(file); int nb_string_tags; file >> nb_string_tags; std::getline(file, line); for (int tag = 0; tag < nb_string_tags; ++tag) { std::getline(file, line); } int nb_real_tags; file >> nb_real_tags; for (int tag = 0; tag < nb_real_tags; ++tag) { double value; file >> value; } int nb_integer_tags; file >> nb_integer_tags; std::vector integer_tags(nb_integer_tags); for (int& tag : integer_tags) { file >> tag; } EXPECT_EQ(integer_tags.size(), 4); NodeData data; data.components = integer_tags[1]; const int nb_entries = integer_tags[2]; data.indices.reserve(nb_entries); data.values.reserve(nb_entries * data.components); std::getline(file, line); for (int entry = 0; entry < nb_entries; ++entry) { std::getline(file, line); std::istringstream record(line); gidx_t index; record >> index; data.indices.push_back(index); for (int component = 0; component < data.components; ++component) { std::string value; record >> value; data.values.push_back(std::stod(value)); } } return data; } std::vector read_element_tags(const std::string& path, int requested_dimension) { std::ifstream file(path); std::string line; while (std::getline(file, line) && line != "$Elements") { } EXPECT(file); size_t nb_blocks; size_t nb_elements; gidx_t min_tag; gidx_t max_tag; file >> nb_blocks >> nb_elements >> min_tag >> max_tag; std::getline(file, line); std::vector tags; for (size_t block = 0; block < nb_blocks; ++block) { int dimension; int entity_tag; int element_type; size_t nb_elements_in_block; file >> dimension >> entity_tag >> element_type >> nb_elements_in_block; std::getline(file, line); for (size_t element = 0; element < nb_elements_in_block; ++element) { std::getline(file, line); if (dimension == requested_dimension) { std::istringstream record(line); gidx_t tag; record >> tag; tags.push_back(tag); } } } return tags; } Field field_with_missing_value() { Field field("values", array::make_datatype(), array::make_shape(3)); auto values = array::make_view(field); values(0) = 1.; values(1) = -999.; values(2) = 3.; field.metadata().set("missing_value_type", "equals"); field.metadata().set("missing_value", -999.); return field; } Field field_with_approximate_missing_value() { Field field("values", array::make_datatype(), array::make_shape(3)); auto values = array::make_view(field); values(0) = 1.; values(1) = 9999.4; values(2) = 3.; field.metadata().set("missing_value_type", "approximately-equals"); field.metadata().set("missing_value", 9999.); field.metadata().set("missing_value_epsilon", 1.); return field; } Field vector_field_with_missing_value() { Field field("vector_values", array::make_datatype(), array::make_shape(2, 2)); field.set_variables(2); auto values = array::make_view(field); values(0, 0) = 1.; values(0, 1) = 2.; values(1, 0) = 3.; values(1, 1) = -999.; field.metadata().set("missing_value_type", "equals"); field.metadata().set("missing_value", -999.); return field; } Field integer_field_with_missing_value() { Field field("integer_values", array::make_datatype(), array::make_shape(2)); auto values = array::make_view(field); values(0) = 1; values(1) = -999; field.metadata().set("missing_value_type", "equals"); field.metadata().set("missing_value", -999); return field; } util::Config missing_value_config(const std::string& policy, double fill = 0.) { util::Config missing_value; missing_value.set("policy", policy); if (policy == "fill") { missing_value.set("fill", fill); } return util::Config("missing_value", missing_value); } util::Config masked_value_config(const std::string& policy, double fill = 0.) { util::Config masked_value; masked_value.set("policy", policy); if (policy == "fill") { masked_value.set("fill", fill); } return util::Config("masked_value", masked_value); } } // namespace //----------------------------------------------------------------------------- CASE("test_gmsh_output_1") { Mesh mesh = test::generate_mesh(Grid("N32")); mesh.metadata().set("part", 2); mesh.metadata().set("nb_parts", 4); output::Gmsh gmsh("test_gmsh_output_1.msh", util::Config("element_partition_as_entity", true)); gmsh.write(mesh); std::ifstream file("test_gmsh_output_1.msh"); std::string line; std::getline(file, line); EXPECT_EQ(line, "$MeshFormat"); std::getline(file, line); EXPECT_EQ(line, "4.1 0 " + std::to_string(sizeof(size_t))); while (std::getline(file, line) && line != "$Entities") { } EXPECT(file); int nb_points; int nb_curves; int nb_surfaces; int nb_volumes; file >> nb_points >> nb_curves >> nb_surfaces >> nb_volumes; EXPECT_EQ(nb_points, 0); EXPECT_EQ(nb_curves, 0); EXPECT_EQ(nb_surfaces, 2); EXPECT_EQ(nb_volumes, 0); std::getline(file, line); for (int expected_owner : {0, 2}) { std::getline(file, line); std::istringstream entity_record(line); int entity_tag; entity_record >> entity_tag; EXPECT_EQ(entity_tag, expected_owner); } while (std::getline(file, line) && line != "$Nodes") { } EXPECT(file); size_t nb_node_blocks; size_t nb_nodes; gidx_t min_node_tag; gidx_t max_node_tag; file >> nb_node_blocks >> nb_nodes >> min_node_tag >> max_node_tag; EXPECT_EQ(nb_node_blocks, 1); EXPECT_EQ(nb_nodes, mesh.nodes().size()); int entity_dimension; int entity_tag; int parametric; size_t nb_nodes_in_block; file >> entity_dimension >> entity_tag >> parametric >> nb_nodes_in_block; EXPECT_EQ(entity_dimension, 2); EXPECT_EQ(entity_tag, 2); EXPECT_EQ(parametric, 0); EXPECT_EQ(nb_nodes_in_block, nb_nodes); } CASE("test_gmsh_output_binary") { Mesh mesh = test::generate_mesh(Grid("N16")); util::Config config("binary", true); config.set("info", true); output::Gmsh("test_gmsh_output_binary.msh", config).write(mesh); std::ifstream file("test_gmsh_output_binary.msh", std::ios::binary); std::string line; std::getline(file, line); EXPECT_EQ(line, "$MeshFormat"); std::getline(file, line); EXPECT_EQ(line, "4.1 1 " + std::to_string(sizeof(size_t))); std::ifstream info_file("test_gmsh_output_binary_info.msh", std::ios::binary); std::getline(info_file, line); EXPECT_EQ(line, "$MeshFormat"); std::getline(info_file, line); EXPECT_EQ(line, "4.1 1 " + std::to_string(sizeof(size_t))); int endian_check; info_file.read(reinterpret_cast(&endian_check), sizeof(endian_check)); EXPECT_EQ(endian_check, 1); while (std::getline(info_file, line) && line != "$NodeData") { } EXPECT(info_file); for (int header_line = 0; header_line < 9; ++header_line) { std::getline(info_file, line); } int node_tag; double latitude; info_file.read(reinterpret_cast(&node_tag), sizeof(node_tag)); info_file.read(reinterpret_cast(&latitude), sizeof(latitude)); auto global_index = array::make_view(mesh.nodes().global_index()); auto lonlat = array::make_view(mesh.nodes().lonlat()); EXPECT_EQ(node_tag, global_index(0)); EXPECT_EQ(latitude, lonlat(0, LAT)); } CASE("test_gmsh_preserves_32bit_edge_tags") { Mesh mesh = test::generate_mesh(Grid("N16")); mesh::actions::build_edges(mesh); auto edge_global_index = array::make_view(mesh.edges().global_index()); for (idx_t edge = 0; edge < mesh.edges().size(); ++edge) { edge_global_index(edge) = 10 + 1000 * edge; } output::Gmsh("test_gmsh_output_compact_edges.msh").write(mesh, util::Config("elements", "edges")); auto tags = read_element_tags("test_gmsh_output_compact_edges.msh", 1); std::sort(tags.begin(), tags.end()); REQUIRE(tags.size() >= 2); EXPECT_EQ(tags[0], 10); EXPECT_EQ(tags[1], 1010); } CASE("test_gmsh_recreate_tags with info") { Mesh mesh = test::generate_mesh(Grid("N16")); mesh::actions::build_edges(mesh); output::Gmsh("test_gmsh_output_compact_edges.msh").write(mesh, util::Config("elements", "edges")("info", true)); auto tags = read_element_tags("test_gmsh_output_compact_edges.msh", 1); std::sort(tags.begin(), tags.end()); REQUIRE(tags.size() >= 2); EXPECT_EQ(tags[0], 1); EXPECT_EQ(tags[1], 2); } CASE("test_gmsh_preserves_32bit_edge_tags with info when human_readable is set") { Mesh mesh = test::generate_mesh(Grid("N16")); mesh::actions::build_edges(mesh); auto edge_global_index = array::make_view(mesh.edges().global_index()); for (idx_t edge = 0; edge < mesh.edges().size(); ++edge) { edge_global_index(edge) = 10 + 1000 * edge; } mesh.edges().global_index().metadata().set("human_readable", true); output::Gmsh("test_gmsh_output_compact_edges.msh").write(mesh, util::Config("elements", "edges")); auto tags = read_element_tags("test_gmsh_output_compact_edges.msh", 1); std::sort(tags.begin(), tags.end()); REQUIRE(tags.size() >= 2); EXPECT_EQ(tags[0], 10); EXPECT_EQ(tags[1], 1010); } CASE("test_gmsh_selects_cell_or_edge_elements") { Mesh mesh = test::generate_mesh(Grid("N16")); mesh::actions::build_edges(mesh); output::Gmsh("test_gmsh_output_auto.msh").write(mesh); EXPECT(!read_element_tags("test_gmsh_output_auto.msh", 2).empty()); EXPECT(read_element_tags("test_gmsh_output_auto.msh", 1).empty()); output::Gmsh("test_gmsh_output_cells.msh").write(mesh, util::Config("elements", "cells")); EXPECT(!read_element_tags("test_gmsh_output_cells.msh", 2).empty()); EXPECT(read_element_tags("test_gmsh_output_cells.msh", 1).empty()); output::Gmsh("test_gmsh_output_edges.msh").write(mesh, util::Config("elements", "edges")); EXPECT(read_element_tags("test_gmsh_output_edges.msh", 2).empty()); EXPECT(!read_element_tags("test_gmsh_output_edges.msh", 1).empty()); mesh.cells().clear(); output::Gmsh("test_gmsh_output_auto_edges.msh").write(mesh); EXPECT(read_element_tags("test_gmsh_output_auto_edges.msh", 2).empty()); EXPECT(!read_element_tags("test_gmsh_output_auto_edges.msh", 1).empty()); mesh.edges().clear(); output::Gmsh("test_gmsh_output_no_elements.msh").write(mesh); EXPECT(read_element_tags("test_gmsh_output_no_elements.msh", 2).empty()); EXPECT(read_element_tags("test_gmsh_output_no_elements.msh", 1).empty()); EXPECT_THROWS(output::Gmsh("test_gmsh_output_invalid.msh").write(mesh, util::Config("elements", "both"))); } CASE("test_gmsh_output_canonical_entities") { Mesh mesh = test::generate_mesh(Grid("N16")); mesh.metadata().set("part", 2); mesh.metadata().set("nb_parts", 4); output::Gmsh("test_gmsh_output_canonical.msh", util::Config("element_partition_as_entity", false)).write(mesh); std::ifstream file("test_gmsh_output_canonical.msh"); std::string contents((std::istreambuf_iterator(file)), std::istreambuf_iterator()); EXPECT(contents.find("$PartitionedEntities\n4\n0\n0 0 1 0\n4 2 1 1 2 ") != std::string::npos); EXPECT(contents.find("$Nodes\n1 ") != std::string::npos); EXPECT(contents.find("\n2 4 0 ") != std::string::npos); } CASE("test_gmsh_output_ghost_elements") { Mesh mesh = test::generate_mesh(Grid("N16")); mesh.metadata().set("part", 2); mesh.metadata().set("nb_parts", 4); mesh::Elements& elements = mesh.cells().elements(0); auto flags = elements.view(elements.flags()); auto halo = elements.view(elements.halo()); auto partition = elements.view(elements.partition()); util::Topology::set(flags(0), util::Topology::GHOST); halo(0) = 1; partition(0) = 0; util::Config owner_entity_config("element_partition_as_entity", true); output::Gmsh("test_gmsh_output_without_ghost.msh", owner_entity_config).write(mesh); std::ifstream without_ghost_file("test_gmsh_output_without_ghost.msh"); std::string without_ghost((std::istreambuf_iterator(without_ghost_file)), std::istreambuf_iterator()); owner_entity_config.set("ghost", true); output::Gmsh("test_gmsh_output_ghost.msh", owner_entity_config).write(mesh); std::ifstream file("test_gmsh_output_ghost.msh"); std::string contents((std::istreambuf_iterator(file)), std::istreambuf_iterator()); auto element_count = [](const std::string& mesh_contents) { std::istringstream stream(mesh_contents); std::string line; while (std::getline(stream, line) && line != "$Elements") { } size_t nb_blocks; size_t nb_elements; stream >> nb_blocks >> nb_elements; return nb_elements; }; EXPECT_EQ(element_count(contents), element_count(without_ghost) + 1); EXPECT(without_ghost.find("$GhostElements") == std::string::npos); EXPECT(contents.find("$PartitionedEntities") == std::string::npos); const auto elements_begin = contents.find("$Elements\n"); const auto elements_end = contents.find("$EndElements\n", elements_begin); EXPECT(elements_begin != std::string::npos); EXPECT(elements_end != std::string::npos); EXPECT(contents.substr(elements_begin, elements_end - elements_begin).find("\n2 0 ") != std::string::npos); EXPECT(contents.find("$GhostElements") == std::string::npos); EXPECT(contents.find("$ElementData") == std::string::npos); util::Config canonical_config("ghost", true); canonical_config.set("element_partition_as_entity", false); output::Gmsh("test_gmsh_output_ghost_canonical.msh", canonical_config).write(mesh); std::ifstream canonical_file("test_gmsh_output_ghost_canonical.msh"); std::string canonical((std::istreambuf_iterator(canonical_file)), std::istreambuf_iterator()); EXPECT(canonical.find("$GhostElements") == std::string::npos); std::istringstream canonical_stream(canonical.substr(canonical.find("$Elements\n") + 10)); size_t nb_blocks; size_t nb_elements; gidx_t min_element_tag; gidx_t max_element_tag; canonical_stream >> nb_blocks >> nb_elements >> min_element_tag >> max_element_tag; bool found_local_entity = false; bool found_owner_entity = false; for (size_t block = 0; block < nb_blocks; ++block) { int dimension; int entity_tag; int element_type; size_t block_size; canonical_stream >> dimension >> entity_tag >> element_type >> block_size; EXPECT_EQ(dimension, 2); found_local_entity |= entity_tag == 4; found_owner_entity |= entity_tag == 2; std::string line; std::getline(canonical_stream, line); for (size_t element = 0; element < block_size; ++element) { std::getline(canonical_stream, line); } } EXPECT(found_local_entity); EXPECT(found_owner_entity); util::Config binary_config("ghost", true); binary_config.set("binary", true); binary_config.set("element_partition_as_entity", true); output::Gmsh("test_gmsh_output_ghost_binary.msh", binary_config).write(mesh); std::ifstream binary_file("test_gmsh_output_ghost_binary.msh", std::ios::binary); std::getline(binary_file, contents); EXPECT_EQ(contents, "$MeshFormat"); std::getline(binary_file, contents); EXPECT_EQ(contents, "4.1 1 " + std::to_string(sizeof(size_t))); std::string binary_contents((std::istreambuf_iterator(binary_file)), std::istreambuf_iterator()); EXPECT(binary_contents.find("$ElementData") == std::string::npos); } 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 ); } CASE("test_gmsh_missing_value_policy") { SECTION("skip is the default") { output::Gmsh("test_gmsh_missing_skip.msh").write(field_with_missing_value()); const auto data = read_scalar_node_data("test_gmsh_missing_skip.msh"); EXPECT_EQ(data.components, 1); EXPECT_EQ(data.indices, std::vector({1, 3})); EXPECT_EQ(data.values, std::vector({1., 3.})); } SECTION("fill") { output::Gmsh("test_gmsh_missing_fill.msh", missing_value_config("fill", -7.5)) .write(field_with_missing_value()); const auto data = read_scalar_node_data("test_gmsh_missing_fill.msh"); EXPECT_EQ(data.components, 1); EXPECT_EQ(data.indices, std::vector({1, 2, 3})); EXPECT_EQ(data.values, std::vector({1., -7.5, 3.})); } SECTION("skip approximately equal value") { output::Gmsh("test_gmsh_missing_approximate_skip.msh").write(field_with_approximate_missing_value()); const auto data = read_scalar_node_data("test_gmsh_missing_approximate_skip.msh"); EXPECT_EQ(data.indices, std::vector({1, 3})); EXPECT_EQ(data.values, std::vector({1., 3.})); } SECTION("fill defaults to zero") { output::Gmsh("test_gmsh_missing_fill_default.msh", missing_value_config("fill")) .write(field_with_missing_value()); const auto data = read_scalar_node_data("test_gmsh_missing_fill_default.msh"); EXPECT_EQ(data.values, std::vector({1., 0., 3.})); } SECTION("write config overrides constructor fill policy") { output::Gmsh gmsh("test_gmsh_missing_write_skip.msh", missing_value_config("fill", -7.5)); gmsh.write(field_with_missing_value(), missing_value_config("skip")); const auto data = read_scalar_node_data("test_gmsh_missing_write_skip.msh"); EXPECT_EQ(data.indices, std::vector({1, 3})); EXPECT_EQ(data.values, std::vector({1., 3.})); } SECTION("write config overrides constructor skip policy") { output::Gmsh gmsh("test_gmsh_missing_write_fill.msh", missing_value_config("skip")); gmsh.write(field_with_missing_value(), missing_value_config("fill", -7.5)); const auto data = read_scalar_node_data("test_gmsh_missing_write_fill.msh"); EXPECT_EQ(data.indices, std::vector({1, 2, 3})); EXPECT_EQ(data.values, std::vector({1., -7.5, 3.})); } SECTION("nan") { output::Gmsh("test_gmsh_missing_nan.msh", missing_value_config("nan")).write(field_with_missing_value()); const auto data = read_scalar_node_data("test_gmsh_missing_nan.msh"); EXPECT_EQ(data.components, 1); EXPECT_EQ(data.indices, std::vector({1, 2, 3})); EXPECT_EQUAL(data.values[0], 1.); EXPECT(std::isnan(data.values[1])); EXPECT_EQUAL(data.values[2], 3.); } SECTION("preserve") { output::Gmsh("test_gmsh_missing_preserve.msh", missing_value_config("preserve")) .write(field_with_missing_value()); const auto data = read_scalar_node_data("test_gmsh_missing_preserve.msh"); EXPECT_EQ(data.indices, std::vector({1, 2, 3})); EXPECT_EQ(data.values, std::vector({1., -999., 3.})); } SECTION("fill replaces only missing vector components") { output::Gmsh("test_gmsh_missing_vector_fill.msh", missing_value_config("fill", -7.5)) .write(vector_field_with_missing_value()); const auto data = read_scalar_node_data("test_gmsh_missing_vector_fill.msh"); EXPECT_EQ(data.components, 3); EXPECT_EQ(data.indices, std::vector({1, 2})); EXPECT_EQ(data.values, std::vector({1., 2., 0., 3., -7.5, 0.})); } SECTION("invalid policy is rejected") { EXPECT_THROWS(output::Gmsh("test_gmsh_missing_invalid.msh", missing_value_config("invalid")) .write(field_with_missing_value())); } SECTION("nan is rejected for integer fields") { EXPECT_THROWS(output::Gmsh("test_gmsh_missing_integer_nan.msh", missing_value_config("nan")) .write(integer_field_with_missing_value())); } } CASE("test_gmsh_masked_value_policy") { auto write_masked_field = [](const std::string& path, const util::Config& config) { Mesh mesh = test::generate_mesh(Grid("O8")); functionspace::NodeColumns functionspace(mesh); functionspace.setMask([](int* mask, size_t size) { std::fill(mask, mask + size, 1); mask[1] = 0; }); Field field = functionspace.createField(option::name("values")); auto values = array::make_view(field); for (idx_t n = 0; n < values.size(); ++n) { values(n) = static_cast(n + 1); } output::Gmsh(path).write(field, config); return read_scalar_node_data(path); }; SECTION("skip is the default") { const auto data = write_masked_field("test_gmsh_masked_preserve.msh", util::Config()); EXPECT(std::find(data.values.begin(), data.values.end(), 2.) == data.values.end()); } SECTION("skip") { const auto data = write_masked_field("test_gmsh_masked_skip.msh", masked_value_config("skip")); EXPECT(std::find(data.values.begin(), data.values.end(), 2.) == data.values.end()); } SECTION("fill") { const auto data = write_masked_field("test_gmsh_masked_fill.msh", masked_value_config("fill", -7.5)); EXPECT_EQ(data.values[1], -7.5); } SECTION("fill takes precedence over missing-value skip") { Mesh mesh = test::generate_mesh(Grid("O8")); functionspace::NodeColumns functionspace(mesh); functionspace.setMask([](int* mask, size_t size) { std::fill(mask, mask + size, 1); mask[0] = 0; }); Field field = functionspace.createField(option::name("values")); auto values = array::make_view(field); std::fill(values.data(), values.data() + values.size(), 1.); values(0) = -999.; field.metadata().set("missing_value_type", "equals"); field.metadata().set("missing_value", -999.); output::Gmsh("test_gmsh_masked_missing_fill.msh").write(field, masked_value_config("fill", -7.5)); const auto data = read_scalar_node_data("test_gmsh_masked_missing_fill.msh"); EXPECT_EQ(data.values[0], -7.5); } SECTION("masked and missing values use different fill values") { Mesh mesh = test::generate_mesh(Grid("O8")); functionspace::NodeColumns functionspace(mesh); functionspace.setMask([](int* mask, size_t size) { std::fill(mask, mask + size, 1); mask[0] = 0; }); Field field = functionspace.createField(option::name("values")); auto values = array::make_view(field); std::fill(values.data(), values.data() + values.size(), 1.); values(1) = -999.; field.metadata().set("missing_value_type", "equals"); field.metadata().set("missing_value", -999.); const util::Config config = masked_value_config("fill", -7.5) | missing_value_config("fill", -8.5); output::Gmsh("test_gmsh_masked_missing_different_fill.msh").write(field, config); const auto data = read_scalar_node_data("test_gmsh_masked_missing_different_fill.msh"); EXPECT_EQ(data.values[0], -7.5); EXPECT_EQ(data.values[1], -8.5); } SECTION("nan") { const auto data = write_masked_field("test_gmsh_masked_nan.msh", masked_value_config("nan")); EXPECT(std::isnan(data.values[1])); } SECTION("invalid policy is rejected") { EXPECT_THROWS(write_masked_field("test_gmsh_masked_invalid.msh", masked_value_config("invalid"))); } SECTION("nan is rejected for integer fields") { Mesh mesh = test::generate_mesh(Grid("O8")); functionspace::NodeColumns functionspace(mesh); functionspace.setMask([](int* mask, size_t size) { std::fill(mask, mask + size, 1); mask[0] = 0; }); Field field = functionspace.createField(option::name("values")); EXPECT_THROWS(output::Gmsh("test_gmsh_masked_integer_nan.msh") .write(field, masked_value_config("nan"))); } SECTION("does not allocate an unset mask") { Mesh mesh = test::generate_mesh(Grid("O8")); functionspace::NodeColumns functionspace(mesh); EXPECT(!functionspace.hasMask()); Field field = functionspace.createField(option::name("values")); auto values = array::make_view(field); std::fill(values.data(), values.data() + values.size(), 1.); output::Gmsh("test_gmsh_masked_unset.msh").write(field, masked_value_config("fill")); EXPECT(!functionspace.hasMask()); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } ecmwf-atlas-b0f79ee/src/tests/acc/0000775000175000017500000000000015263665111017271 5ustar alastairalastairecmwf-atlas-b0f79ee/src/tests/acc/fctest_unified_memory_with_openacc_cxx.cc0000664000175000017500000000102615263665111027567 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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))); } } ecmwf-atlas-b0f79ee/src/tests/acc/fctest_unified_memory_with_openacc.F900000664000175000017500000000435515263665111026666 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/acc/CMakeLists.txt0000664000175000017500000000222215263665111022027 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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() ecmwf-atlas-b0f79ee/src/tests/acc/fctest_connectivity_openacc.F900000664000175000017500000002201015263665111025322 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/array/0000775000175000017500000000000015263665111017661 5ustar alastairalastairecmwf-atlas-b0f79ee/src/tests/array/test_array_view_variant.cc0000664000175000017500000000746015263665111025132 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); } ecmwf-atlas-b0f79ee/src/tests/array/test_array.cc0000664000175000017500000004171415263665111022354 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/array/CMakeLists.txt0000664000175000017500000000451115263665111022422 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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() ecmwf-atlas-b0f79ee/src/tests/array/test_vector_kernel.hic0000664000175000017500000000523215263665111024251 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 ); } ecmwf-atlas-b0f79ee/src/tests/array/test_array_slicer.cc0000664000175000017500000003142615263665111023714 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/array/test_table.cc0000664000175000017500000001363015263665111022321 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/array/test_svector_kernel.hic0000664000175000017500000000470415263665111024437 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 ); } ecmwf-atlas-b0f79ee/src/tests/array/test_array_foreach.cc0000664000175000017500000004061115263665111024036 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); } ecmwf-atlas-b0f79ee/src/tests/array/test_array_vector.cc0000664000175000017500000000266415263665111023737 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/array/test_indexview.cc0000664000175000017500000001630615263665111023237 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/array/test_array_kernel.hic0000664000175000017500000000763015263665111024071 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 ); } ecmwf-atlas-b0f79ee/src/tests/array/test_array_view_util.cc0000664000175000017500000000346015263665111024437 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/array/test_svector.cc0000664000175000017500000000270315263665111022716 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/grid/0000775000175000017500000000000015263665111017470 5ustar alastairalastairecmwf-atlas-b0f79ee/src/tests/grid/fctest_grids.F900000664000175000017500000000411615263665111022432 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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_expected, json grid = atlas_Grid ("O32") FCTEST_CHECK_EQUAL( grid%type(), "structured" ) spec = grid%spec() FCTEST_CHECK_EQUAL( spec%owners(), 1 ) json_expected = '{"name":"O32"}' json = spec%json() FCTEST_CHECK( json == json_expected ) 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 ecmwf-atlas-b0f79ee/src/tests/grid/fctest_state.F900000664000175000017500000001002115263665111022432 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/grid/test_state.cc0000664000175000017500000001532115263665111022160 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/grid/test_field.cc0000664000175000017500000001347615263665111022134 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/grid/test_cubedsphere_2.cc0000664000175000017500000000713315263665111023554 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); } ecmwf-atlas-b0f79ee/src/tests/grid/test_vertical.cc0000664000175000017500000000332115263665111022646 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/grid/fctest_stretchedrotatedgaussiangrid.F900000664000175000017500000033604215263665111027301 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/tests/grid/CMakeLists.txt0000664000175000017500000000502315263665111022230 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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() ecmwf-atlas-b0f79ee/src/tests/grid/test_largegrid.cc0000664000175000017500000000425315263665111023002 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); } ecmwf-atlas-b0f79ee/src/tests/grid/fctest_unstructuredgrid.F900000664000175000017500000002023015263665111024732 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 fixture_unstructuredgrid use, intrinsic :: iso_c_binding, only : c_double implicit none real(c_double) :: xy(2,173) contains subroutine init_xy() 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] end subroutine end module ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fctest_unstructuredgrid, fixture_unstructuredgrid) ! ----------------------------------------------------------------------------- TESTSUITE_INIT use atlas_module call atlas_library%initialise() call init_xy() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE use atlas_module call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ---------------------------------------------------------------------------- TEST( test_unstructuredgrid ) use atlas_module implicit none type(atlas_UnstructuredGrid) :: grid type(atlas_Mesh) :: mesh type(atlas_Output) :: gmsh type(atlas_MeshGenerator) :: meshgenerator 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 ! ----------------------------------------------------------------------------- TEST( test_unstructuredgrid_x_and_y_mesh_constructor ) use, intrinsic :: iso_c_binding use atlas_module implicit none type(atlas_UnstructuredGrid) :: grid type(atlas_Mesh) :: mesh type(atlas_mesh_Nodes) :: nodes type(atlas_Field) :: lonlat real(c_double), allocatable :: x(:), y(:) real(c_double), pointer :: lonlat_view(:,:) integer(c_int) :: grid_size grid_size = size(xy,2) x = xy(1,:) y = xy(2,:) grid = atlas_UnstructuredGrid(x, y) FCTEST_CHECK_EQUAL( int(grid%size(), c_int), grid_size ) FCTEST_CHECK_EQUAL( grid%owners(), 1 ) mesh = atlas_Mesh(grid) nodes = mesh%nodes() lonlat = nodes%lonlat() call lonlat%data(lonlat_view) FCTEST_CHECK_EQUAL( mesh%owners(), 1 ) FCTEST_CHECK_EQUAL( grid%owners(), 2 ) FCTEST_CHECK_EQUAL( int(nodes%size(), c_int), grid_size ) FCTEST_CHECK_CLOSE( lonlat_view(1,:), x, 1.e-12_c_double ) FCTEST_CHECK_CLOSE( lonlat_view(2,:), y, 1.e-12_c_double ) call lonlat%final() call nodes%final() call mesh%final() FCTEST_CHECK_EQUAL( grid%owners(), 1 ) call grid%final() END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE ecmwf-atlas-b0f79ee/src/tests/grid/test_grids.cc0000664000175000017500000007173115263665111022157 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/runtime/Exception.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; EXPECT(grid.flags(0) == GridPointFlags::none); EXPECT(grid.masterIndex(0) == 0); } CASE("test_factory_from_grid_key") { const Grid named("O32"); const Grid from_grid_key(Config("grid", "O32")); EXPECT(from_grid_key.uid() == named.uid()); } CASE("test_factory_from_json_spec") { const Grid named("O32"); EXPECT(not named.spec().has("domain")); const Grid named_from_json(" \n" + named.spec().json()); EXPECT(named_from_json.uid() == named.uid()); const Grid shifted(named, RectangularDomain({-180., 180.}, {-90., 90.})); EXPECT(shifted.spec().has("domain")); const Grid shifted_from_json(shifted.spec().json()); EXPECT(shifted_from_json.uid() == shifted.uid()); const Config configured_spec = Config("type", "structured") ("xspace", Config("type", "linear")("N", 16)("start", 0.)("end", 360.)("endpoint", false)) ("yspace", Config("type", "linear")("N", 9)("start", 90.)("end", -90.)); const Grid configured(configured_spec); const Grid configured_from_json(configured.spec().json()); EXPECT(configured_from_json.uid() == configured.uid()); } CASE("test_factory_domain_roundtrip") { const Grid named("O32"); const Grid regional( Config("type", "structured") ("xspace", Config("type", "linear")("N", 16)("start", -10.)("end", 10.)) ("yspace", Config("type", "linear")("N", 9)("start", 60.)("end", 40.))); const Grid cropped(named, RectangularDomain({-10., 10.}, {40., 60.})); EXPECT(not named.spec().has("domain")); EXPECT(not regional.spec().has("domain")); EXPECT(cropped.spec().has("domain")); for (const Grid& original : {named, regional, cropped}) { EXPECT(bool(original.domain())); for (const Grid& reconstructed : {Grid(original.spec()), Grid(original.spec().json())}) { EXPECT(bool(reconstructed.domain())); EXPECT_EQ(reconstructed.domain().spec().json(), original.domain().spec().json()); EXPECT_EQ(reconstructed.size(), original.size()); EXPECT_EQ(reconstructed.uid(), original.uid()); } } } CASE("test_saber_missing_domain_reproduction") { const StructuredGrid work_grid( Config("type", "structured") ("xspace", Config("type", "linear")("N", 16)("start", -10.)("end", 10.)) ("yspace", Config("type", "linear")("N", 9)("start", 60.)("end", 40.))); const Config work_grid_spec = work_grid.spec(); Config domain_spec; const bool legacy_saber_domain_found = work_grid_spec.get("domain", domain_spec); EXPECT(not legacy_saber_domain_found); if (!legacy_saber_domain_found) { Log::info() << "Reproduced legacy SABER abort condition: geom_from_atlas: missing domain" << std::endl; // SABER USED to fail here trying to access the domain_spec, see comments in https://github.com/ecmwf/atlas/pull/425, but that was now fixed in SABER: // https://github.com/JCSDA/saber/commit/cdcf0170273e6dda8ddddd980e1c525faf44a175 -- Fix failing ctests due to removed domain/projection defaults in Atlas (#1325) } // A backward compatible spec was created in SABER: Config compatible_spec(work_grid_spec); compatible_spec.set("domain", work_grid.domain().spec()); EXPECT(compatible_spec.get("domain", domain_spec)); EXPECT_EQ(domain_spec.getString("type"), "rectangular"); EXPECT_EQ(Grid(compatible_spec).uid(), work_grid.uid()); const RectangularDomain domain(work_grid.domain()); EXPECT(bool(domain)); EXPECT_EQ(domain.xmin(), -10.); EXPECT_EQ(domain.xmax(), 10.); EXPECT_EQ(domain.ymin(), 40.); EXPECT_EQ(domain.ymax(), 60.); } 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); } ecmwf-atlas-b0f79ee/src/tests/grid/test_distribution_regular_bands.cc0000664000175000017500000001635615263665111026460 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/grid/test_grid_hash.cc0000664000175000017500000000355015263665111022771 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/grid/test_grid_iterator.cc0000664000175000017500000000614615263665111023703 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/grid/test_stretchedrotatedgaussian.cc0000664000175000017500000024273115263665111026152 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); } ecmwf-atlas-b0f79ee/src/tests/grid/test_grid_cropping.cc0000664000175000017500000002065415263665111023673 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/grid/fctest_regional_grids.F900000664000175000017500000001407615263665111024320 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/grid/test_cubedsphere.cc0000664000175000017500000005556215263665111023344 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); } ecmwf-atlas-b0f79ee/src/tests/grid/test_spacing.cc0000664000175000017500000001075415263665111022471 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/grid/test_domain.cc0000664000175000017500000001167215263665111022314 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/grid/fctest_griddistribution.F900000664000175000017500000000622015263665111024705 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/export_tests/0000775000175000017500000000000015263665111021306 5ustar alastairalastairecmwf-atlas-b0f79ee/src/tests/export_tests/CMakeLists.txt0000664000175000017500000000126315263665111024050 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 ) ecmwf-atlas-b0f79ee/src/tests/parallel/0000775000175000017500000000000015263665111020337 5ustar alastairalastairecmwf-atlas-b0f79ee/src/tests/parallel/test_mpi_scope.cc0000664000175000017500000000737215263665111023674 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" ); { mpi::scope::push(mpi::comm("self")); EXPECT_EQ( mpi::comm().name(), "self" ); mpi::scope::pop(); } 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); } ecmwf-atlas-b0f79ee/src/tests/parallel/CMakeLists.txt0000664000175000017500000000423615263665111023104 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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} ) ecmwf-atlas-b0f79ee/src/tests/parallel/test_omp_sort.cc0000664000175000017500000000613715263665111023556 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/parallel/test_haloexchange.cc0000664000175000017500000006312215263665111024337 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/parallel/test_sync.F900000664000175000017500000001002115263665111022624 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/parallel/test_haloexchange_adjoint.cc0000664000175000017500000015135215263665111026052 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); } ecmwf-atlas-b0f79ee/src/tests/parallel/test_setcomm.cc0000664000175000017500000000370615263665111023362 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); } ecmwf-atlas-b0f79ee/src/tests/parallel/test_collect.cc0000664000175000017500000001373715263665111023345 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/parallel/test_gather.cc0000664000175000017500000011263615263665111023170 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/system/ResourceUsage.h" #include "eckit/utils/Translator.h" #include "tests/AtlasTestEnvironment.h" /// POD: Type to test using POD = double; namespace atlas { namespace test { constexpr size_t MB = 1024 * 1024; constexpr size_t GB = 1024 * MB; size_t peakMemory() { return eckit::system::ResourceUsage().maxResidentSetSize(); } //----------------------------------------------------------------------------- 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; } }; struct SparseGlobalIndexFixture { SparseGlobalIndexFixture() { rank = static_cast(mpi::comm().rank()); comm_size = static_cast(mpi::comm().size()); int nnodes_c[] = {4, 4, 4}; nb_nodes = vec(nnodes_c); Nl = nb_nodes[rank]; switch (rank) { case 0: { int part_c[] = {0, 0, 1, 2}; part = vec(part_c); idx_t ridx_c[] = {0, 1, 0, 0}; ridx = vec(ridx_c); gidx_t gidx_c[] = {10, 30, 20, 40}; gidx = vec(gidx_c); break; } case 1: { int part_c[] = {1, 1, 0, 2}; part = vec(part_c); idx_t ridx_c[] = {0, 1, 1, 1}; ridx = vec(ridx_c); gidx_t gidx_c[] = {20, 50, 30, 1439999999}; gidx = vec(gidx_c); break; } case 2: { int part_c[] = {2, 2, 0, 1}; part = vec(part_c); idx_t ridx_c[] = {0, 1, 0, 1}; ridx = vec(ridx_c); gidx_t gidx_c[] = {40, 1439999999, 10, 50}; 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; } }; struct SetupOptionsFixture { SetupOptionsFixture() { rank = static_cast(mpi::comm().rank()); EXPECT_EQ(mpi::comm().size(), 3); switch (rank) { case 0: part = {0, 0, 1, 2}; remote_idx = {0, 1, 0, 0}; dense_gidx = {1, 4, 2, 3}; break; case 1: part = {1, 1, 0, 2}; remote_idx = {0, 1, 0, 0}; dense_gidx = {2, 5, 1, 3}; break; case 2: part = {2, 2, 0, 1}; remote_idx = {0, 1, 0, 0}; dense_gidx = {3, 6, 1, 2}; break; } mask = {0, 0, 1, 1}; sparse_gidx.reserve(dense_gidx.size()); constexpr gidx_t sparse_indices[] = {0, 2, 4, 7, 9, 11, 13}; for (const gidx_t gidx : dense_gidx) { sparse_gidx.emplace_back(sparse_indices[gidx]); } } int rank; std::vector part; std::vector remote_idx; std::vector dense_gidx; std::vector sparse_gidx; std::vector mask; }; void test_mapping(parallel::GatherScatter& gather_scatter, const SetupOptionsFixture& fixture, const std::vector& global_index, const std::vector& mask, const std::vector& expected_global, gidx_t expected_dof) { EXPECT_EQ(gather_scatter.glb_dof(), expected_dof); for (int root = 0; root < 3; ++root) { std::vector local(global_index.size(), -1.); for (idx_t index = 0; index < local.size(); ++index) { if (!mask[index]) { local[index] = static_cast(global_index[index] * 10); } } std::vector global(fixture.rank == root ? expected_global.size() : 0, -1.); idx_t strides[] = {1}; idx_t extents[] = {1}; gather_scatter.gather(local.data(), strides, extents, 1, global.data(), strides, extents, 1, root); if (fixture.rank == root) { EXPECT(global == expected_global); } local.assign(local.size(), -1.); gather_scatter.scatter(global.data(), strides, extents, 1, local.data(), strides, extents, 1, root); for (idx_t index = 0; index < local.size(); ++index) { if (!mask[index]) { EXPECT_EQ(local[index], static_cast(global_index[index] * 10)); } else { EXPECT_EQ(local[index], -1.); } } } } //----------------------------------------------------------------------------- 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; } } } } } CASE("test_gather_sparse_global_indices") { SparseGlobalIndexFixture f; // The fixture includes global-index 14399999990. // An 64-bit-value array of this size (a global gathered field) would // require more than 100GB. constexpr size_t sparse_global_index_memory_limit = 10 * GB; // less than 100 GB 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 ? -1. : 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); EXPECT(f.gather_scatter.glb_dof() == 6); if (f.rank == f.root) { POD glb_c[] = {100, 200, 300, 400, 500, 14399999990.}; EXPECT(glb == eckit::testing::make_view(glb_c, glb_c + f.Ng())); } } Log::warning() << "test_gather_sparse_global_indices: peak memory usage = " << peakMemory() / MB << " MB" << std::endl; EXPECT(peakMemory() < sparse_global_index_memory_limit); } CASE("test_gather_setup_options") { SetupOptionsFixture fixture; const idx_t size = static_cast(fixture.part.size()); const std::vector dense_expected{10., 20., 30., 40., 50., 60.}; const std::vector sparse_compacted_expected{20., 40., 70., 90., 110., 130.}; const std::vector sparse_preserved_expected{-1., 20., -1., 40., -1., -1., 70., -1., 90., -1., 110., -1., 130.}; SECTION("automatic mask with default communicator") { parallel::GatherScatter gather_scatter; gather_scatter.setup(fixture.part.data(), fixture.remote_idx.data(), 0, fixture.dense_gidx.data(), size); test_mapping(gather_scatter, fixture, fixture.dense_gidx, fixture.mask, dense_expected, 6); } SECTION("automatic mask with named communicator") { parallel::GatherScatter gather_scatter; gather_scatter.setup(mpi::comm().name(), fixture.part.data(), fixture.remote_idx.data(), 0, fixture.dense_gidx.data(), size); test_mapping(gather_scatter, fixture, fixture.dense_gidx, fixture.mask, dense_expected, 6); } SECTION("explicit mask with default communicator") { const std::vector mask{0, 1, 1, 1}; parallel::GatherScatter gather_scatter; gather_scatter.setup(fixture.part.data(), fixture.remote_idx.data(), 0, fixture.dense_gidx.data(), mask.data(), size); test_mapping(gather_scatter, fixture, fixture.dense_gidx, mask, {10., 20., 30.}, 3); } SECTION("explicit mask with named communicator") { const std::vector mask{0, 1, 1, 1}; parallel::GatherScatter gather_scatter; gather_scatter.setup(mpi::comm().name(), fixture.part.data(), fixture.remote_idx.data(), 0, fixture.dense_gidx.data(), mask.data(), size); test_mapping(gather_scatter, fixture, fixture.dense_gidx, mask, {10., 20., 30.}, 3); } SECTION("remote index base one") { auto remote_idx = fixture.remote_idx; for (auto& index : remote_idx) { ++index; } parallel::GatherScatter gather_scatter; gather_scatter.setup(mpi::comm().name(), fixture.part.data(), remote_idx.data(), 1, fixture.dense_gidx.data(), fixture.mask.data(), size); test_mapping(gather_scatter, fixture, fixture.dense_gidx, fixture.mask, dense_expected, 6); } SECTION("sparse global indices are compacted by default") { parallel::GatherScatter gather_scatter; gather_scatter.setup(mpi::comm().name(), fixture.part.data(), fixture.remote_idx.data(), 0, fixture.sparse_gidx.data(), fixture.mask.data(), size, false); test_mapping(gather_scatter, fixture, fixture.sparse_gidx, fixture.mask, sparse_compacted_expected, 6); } SECTION("sparse global indices can be preserved") { parallel::GatherScatter gather_scatter; gather_scatter.setup(mpi::comm().name(), fixture.part.data(), fixture.remote_idx.data(), 0, fixture.sparse_gidx.data(), fixture.mask.data(), size, true); test_mapping(gather_scatter, fixture, fixture.sparse_gidx, fixture.mask, sparse_preserved_expected, 6); } SECTION("duplicate global indices are deduplicated when compacting") { const std::vector mask(size, 0); parallel::GatherScatter gather_scatter; gather_scatter.setup(mpi::comm().name(), fixture.part.data(), fixture.remote_idx.data(), 0, fixture.dense_gidx.data(), mask.data(), size, false); EXPECT_EQ(gather_scatter.glb_dof(), 6); for (int root = 0; root < 3; ++root) { std::vector local(size); for (idx_t index = 0; index < size; ++index) { local[index] = static_cast(fixture.dense_gidx[index] * 10); } std::vector global(fixture.rank == root ? dense_expected.size() : 0, -1.); idx_t strides[] = {1}; idx_t extents[] = {1}; gather_scatter.gather(local.data(), strides, extents, 1, global.data(), strides, extents, 1, root); if (fixture.rank == root) { EXPECT(global == dense_expected); } } } SECTION("all points masked") { std::vector mask(size, 1); parallel::GatherScatter gather_scatter; gather_scatter.setup(mpi::comm().name(), fixture.part.data(), fixture.remote_idx.data(), 0, fixture.dense_gidx.data(), mask.data(), size); EXPECT_EQ(gather_scatter.glb_dof(), 0); EXPECT_EQ(gather_scatter.loc_dof(), 0); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } ecmwf-atlas-b0f79ee/src/tests/parallel/test_omp_copy.cc0000664000175000017500000000276615263665111023545 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/functionspace/0000775000175000017500000000000015263665111021404 5ustar alastairalastairecmwf-atlas-b0f79ee/src/tests/functionspace/test_structuredcolumns_area.cc0000664000175000017500000001203115263665111027544 0ustar alastairalastair/* * (C) Copyright 2026 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/FieldBuilder.h" #include "atlas/field/Field.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Grid.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/util/Config.h" #include "atlas/util/Geometry.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { namespace { void validate_build_area_field(const Grid& grid) { const double one_square_metre = 1.e6; functionspace::StructuredColumns fs(grid, option::halo(1)); field::FieldBuilder build_area_field{"grid-box-area", fs}; Field area_field = build_area_field(); auto area = array::make_view(area_field); EXPECT_EQ(area_field.name(), "area"); EXPECT_EQ(area_field.shape(0), fs.size()); idx_t checked_rows = 0; for (idx_t j = fs.j_begin_halo(); j < fs.j_end_halo(); ++j) { const idx_t i_begin = fs.i_begin_halo(j); const idx_t i_end = fs.i_end_halo(j); if (i_begin == i_end) { continue; } const double row_area = area(fs.index(i_begin, j)); Log::info() << "Row " << j << " representative area = " << row_area / 1.e6 << " km^2" << std::endl; EXPECT(std::isfinite(row_area)); EXPECT(row_area >= one_square_metre); ++checked_rows; for (idx_t i = i_begin; i < i_end; ++i) { const idx_t point_index = fs.index(i, j); EXPECT_EQ(area(point_index), row_area); } } EXPECT(checked_rows > 0); } Grid rotated_o32() { return Grid("O32", Projection(option::type("rotated_lonlat") | util::Config("north_pole", {-176., 40.}))); } Grid regional_lonlat() { const int Nx = 8, Ny = 8; const double xmin = +20, xmax = +60, ymin = +20, ymax = +60; StructuredGrid::XSpace xspace(util::Config("type", "linear")("N", Nx)("start", xmin)("end", xmax)); StructuredGrid::YSpace yspace(util::Config("type", "linear")("N", Ny)("start", ymin)("end", ymax)); return StructuredGrid(xspace, yspace); } Grid regional_lambert() { util::Config gridspec; gridspec.set("type", "regional"); gridspec.set("nx", 21); gridspec.set("ny", 31); gridspec.set("dx", 8000.); gridspec.set("dy", 9000.); gridspec.set("lonlat(centre)", {9.9, 56.3}); gridspec.set("projection", [] { util::Config projection; projection.set("type", "lambert_conformal_conic"); projection.set("longitude0", 0.); projection.set("latitude0", 56.3); return projection; }()); return Grid(gridspec); } } // namespace CASE("test Grid(O32) area field") { validate_build_area_field(Grid("O32")); } CASE("test global regular lonlat 5 degree area field") { validate_build_area_field(Grid("L72x37")); } CASE("test rotated Grid(O32) area field") { validate_build_area_field(rotated_o32()); } CASE("test regional lonlat area field") { validate_build_area_field(regional_lonlat()); } CASE("test regional lambert area field") { validate_build_area_field(regional_lambert()); } CASE("test grid-box-area configuration") { functionspace::StructuredColumns fs(Grid("O32"), option::halo(1)); const idx_t point_index = fs.index(fs.i_begin(fs.j_begin()), fs.j_begin()); Field default_area = field::FieldBuilder("grid-box-area", fs)(); Field named_area = field::FieldBuilder("grid-box-area", fs, util::Config("field_name", "cell_area"))(); Field radius_area = field::FieldBuilder("grid-box-area", fs, util::Config("radius", 2.))(); Field unit_sphere_area = field::FieldBuilder("grid-box-area", fs, util::Config("geometry", "UnitSphere"))(); Field geometry_overrides_radius = field::FieldBuilder("grid-box-area", fs, util::Config("radius", 2.)("geometry", "UnitSphere"))(); const auto default_view = array::make_view(default_area); const auto radius_view = array::make_view(radius_area); const auto unit_sphere_view = array::make_view(unit_sphere_area); const auto override_view = array::make_view(geometry_overrides_radius); EXPECT_EQ(default_area.name(), "area"); EXPECT_EQ(named_area.name(), "cell_area"); const double earth_radius = geometry::Earth().radius(); EXPECT_APPROX_EQ(radius_view(point_index) / default_view(point_index), 4. / (earth_radius * earth_radius), 1.e-14); EXPECT_APPROX_EQ(unit_sphere_view(point_index) / default_view(point_index), 1. / (earth_radius * earth_radius), 1.e-14); EXPECT_EQ(override_view(point_index), unit_sphere_view(point_index)); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); }ecmwf-atlas-b0f79ee/src/tests/functionspace/test_cubedsphere_functionspace.cc0000664000175000017500000003727715263665111030204 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); } ecmwf-atlas-b0f79ee/src/tests/functionspace/test_pointcloud_haloexchange_2PE.cc0000664000175000017500000002372515263665111030317 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); } ecmwf-atlas-b0f79ee/src/tests/functionspace/test_pointcloud_haloexchange_3PE.cc0000664000175000017500000000726215263665111030316 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); } ecmwf-atlas-b0f79ee/src/tests/functionspace/test_stencil_parallel.cc0000664000175000017500000000650515263665111026275 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/functionspace/test_structuredcolumns.cc0000664000175000017500000007335515263665111026574 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/FieldSet.h" #include "atlas/functionspace/BlockStructuredColumns.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 { namespace { template void fill_structured_field(Field& field, Value first_value = Value{1}) { Value next_value = first_value; if (field.variables() && field.levels()) { auto value = array::make_view(field); for (idx_t point = 0; point < field.shape(0); ++point) { for (idx_t jlev = 0; jlev < field.shape(1); ++jlev) { for (idx_t jvar = 0; jvar < field.shape(2); ++jvar) { value(point, jlev, jvar) = next_value; next_value += Value{1}; } } } } else if (field.variables() || field.levels()) { auto value = array::make_view(field); for (idx_t point = 0; point < field.shape(0); ++point) { for (idx_t entry = 0; entry < field.shape(1); ++entry) { value(point, entry) = next_value; next_value += Value{1}; } } } else { auto value = array::make_view(field); for (idx_t point = 0; point < field.shape(0); ++point) { value(point) = next_value; next_value += Value{1}; } } } } // namespace //----------------------------------------------------------------------------- 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("StructuredColumns checksum matches BlockStructuredColumns for identical data") { using Value = double; mpi::Scope scope("self"); auto grid = StructuredGrid("O8"); auto structured_fs = [&] { util::Config config; config.set("halo", 0); return functionspace::StructuredColumns(grid, config); }(); auto block_fs = [&] { util::Config config; config.set("halo", 0); config.set("nproma", 5); return functionspace::BlockStructuredColumns(grid, config); }(); auto check_field_checksum_stable_with_blocked = [&](const util::Config& options, Value first_value) { Field structured = structured_fs.createField(option::name("structured") | options); Field blocked = block_fs.createField(option::name("blocked") | options); fill_structured_field(structured, first_value); block_fs.scatter(structured, blocked); EXPECT_EQ(structured_fs.checksum(structured), block_fs.checksum(blocked)); }; check_field_checksum_stable_with_blocked(option::variables(3) | option::levels(4), Value{1}); check_field_checksum_stable_with_blocked(option::variables(3), Value{1000}); check_field_checksum_stable_with_blocked(option::levels(4), Value{2000}); check_field_checksum_stable_with_blocked(util::Config{}, Value{3000}); auto check_fieldset_checksum_stable_with_blocked = [&](const util::Config& options_a, const util::Config& options_b) { FieldSet structured_fieldset; FieldSet blocked_fieldset; Field structured_a = structured_fs.createField(option::name("structured_a") | options_a); Field structured_b = structured_fs.createField(option::name("structured_b") | options_b); Field blocked_a = block_fs.createField(option::name("blocked_a") | options_a); Field blocked_b = block_fs.createField(option::name("blocked_b") | options_b); fill_structured_field(structured_a, Value{4000}); fill_structured_field(structured_b, Value{5000}); block_fs.scatter(structured_a, blocked_a); block_fs.scatter(structured_b, blocked_b); structured_fieldset.add(structured_a); structured_fieldset.add(structured_b); blocked_fieldset.add(blocked_a); blocked_fieldset.add(blocked_b); EXPECT_EQ(structured_fs.checksum(structured_fieldset), block_fs.checksum(blocked_fieldset)); }; check_fieldset_checksum_stable_with_blocked(/*a*/ option::variables(2) | option::levels(3), /*b*/ option::variables(4)); } CASE("StructuredColumns checksum matches BlockStructuredColumns in MPI for identical scattered data") { using Value = double; constexpr int root = 0; auto grid = StructuredGrid("O8"); auto structured_fs = [&] { util::Config config; config.set("halo", 0); return functionspace::StructuredColumns(grid, config); }(); auto block_fs = [&] { util::Config config; config.set("halo", 0); config.set("nproma", 5); return functionspace::BlockStructuredColumns(grid, config); }(); auto run_case = [&](const util::Config& options, Value first_value) { Field global = structured_fs.createField(option::name("global") | option::global(root) | options); Field structured = structured_fs.createField(option::name("structured") | options); Field blocked = block_fs.createField(option::name("blocked") | options); if (mpi::comm().rank() == root) { fill_structured_field(global, first_value); } structured_fs.scatter(global, structured); block_fs.scatter(global, blocked); EXPECT_EQ(structured_fs.checksum(structured), block_fs.checksum(blocked)); }; run_case(option::variables(3) | option::levels(4), Value{1}); run_case(option::variables(3), Value{1000}); run_case(option::levels(4), Value{2000}); run_case(util::Config{}, Value{3000}); Field global_a = structured_fs.createField(option::name("global_a") | option::global(root) | option::variables(2) | option::levels(3)); Field global_b = structured_fs.createField(option::name("global_b") | option::global(root) | option::variables(4)); Field structured_a = structured_fs.createField(option::name("structured_a") | option::variables(2) | option::levels(3)); Field structured_b = structured_fs.createField(option::name("structured_b") | option::variables(4)); Field blocked_a = block_fs.createField(option::name("blocked_a") | option::variables(2) | option::levels(3)); Field blocked_b = block_fs.createField(option::name("blocked_b") | option::variables(4)); if (mpi::comm().rank() == root) { fill_structured_field(global_a, Value{4000}); fill_structured_field(global_b, Value{5000}); } structured_fs.scatter(global_a, structured_a); structured_fs.scatter(global_b, structured_b); block_fs.scatter(global_a, blocked_a); block_fs.scatter(global_b, blocked_b); FieldSet structured_fieldset; FieldSet blocked_fieldset; structured_fieldset.add(structured_a); structured_fieldset.add(structured_b); blocked_fieldset.add(blocked_a); blocked_fieldset.add(blocked_b); EXPECT_EQ(structured_fs.checksum(structured_fieldset), block_fs.checksum(blocked_fieldset)); } CASE("StructuredColumns and BlockStructuredColumns checksums are stable with halo=2") { using Value = double; mpi::Scope scope("self"); auto grid = StructuredGrid("O8"); auto structured_fs = [&] { util::Config config; config.set("halo", 2); return functionspace::StructuredColumns(grid, config); }(); auto block_fs = [&] { util::Config config; config.set("halo", 2); config.set("nproma", 5); return functionspace::BlockStructuredColumns(grid, config); }(); util::Config options = option::variables(3) | option::levels(4); Field structured = structured_fs.createField(option::name("structured") | options); fill_structured_field(structured, Value{6000}); FieldSet structured_fieldset; structured_fieldset.add(structured); EXPECT_EQ(structured_fs.checksum(structured), structured_fs.checksum(structured_fieldset)); EXPECT_EQ(structured_fs.checksum(structured), structured_fs.checksum(structured)); Field blocked = block_fs.createField(option::name("blocked") | options); block_fs.scatter(structured, blocked); FieldSet blocked_fieldset; blocked_fieldset.add(blocked); EXPECT_EQ(block_fs.checksum(blocked), block_fs.checksum(blocked_fieldset)); EXPECT_EQ(block_fs.checksum(blocked), block_fs.checksum(blocked)); } CASE("StructuredColumns checksum with halo=2 matches halo=0 exactly (mpi-serial)") { using Value = double; mpi::Scope scope("self"); auto grid = StructuredGrid("O8"); auto fs_h0 = [&] { util::Config config; config.set("halo", 0); return functionspace::StructuredColumns(grid, config); }(); auto fs_h2 = [&] { util::Config config; config.set("halo", 2); return functionspace::StructuredColumns(grid, config); }(); auto check_field_checksum_stable_with_halo = [&](const util::Config& options, Value first_value) { Field field_h0 = fs_h0.createField(option::name("field_h0") | options); Field field_h2 = fs_h2.createField(option::name("field_h2") | options); fill_structured_field(field_h0, first_value); fill_structured_field(field_h2, first_value); EXPECT_EQ(fs_h0.checksum(field_h0), fs_h2.checksum(field_h2)); }; check_field_checksum_stable_with_halo(option::variables(3) | option::levels(4), Value{12000}); check_field_checksum_stable_with_halo(option::variables(3), Value{13000}); check_field_checksum_stable_with_halo(option::levels(4), Value{14000}); check_field_checksum_stable_with_halo(util::Config{}, Value{15000}); auto check_fieldset_checksum_stable_with_halo = [&](const util::Config& options_a, const util::Config& options_b) { Field field_h0_a = fs_h0.createField(option::name("field_h0_a") | options_a); Field field_h0_b = fs_h0.createField(option::name("field_h0_b") | options_b); Field field_h2_a = fs_h2.createField(option::name("field_h2_a") | options_a); Field field_h2_b = fs_h2.createField(option::name("field_h2_b") | options_b); fill_structured_field(field_h0_a, Value{16000}); fill_structured_field(field_h0_b, Value{17000}); fill_structured_field(field_h2_a, Value{16000}); fill_structured_field(field_h2_b, Value{17000}); FieldSet fieldset_h0; FieldSet fieldset_h2; fieldset_h0.add(field_h0_a); fieldset_h0.add(field_h0_b); fieldset_h2.add(field_h2_a); fieldset_h2.add(field_h2_b); EXPECT_EQ(fs_h0.checksum(fieldset_h0), fs_h2.checksum(fieldset_h2)); }; check_fieldset_checksum_stable_with_halo(/*a*/ option::variables(2) | option::levels(3), /*b*/ option::variables(4)); } CASE("StructuredColumns checksum with halo=2 matches halo=0 exactly (MPI)") { using Value = double; constexpr int root = 0; auto grid = StructuredGrid("O8"); auto fs_h0 = [&] { return functionspace::StructuredColumns(grid, option::halo(0)); }(); auto fs_h2 = [&] { return functionspace::StructuredColumns(grid, option::halo(2)); }(); auto check_field_checksum_stable_with_halo = [&](const util::Config& options, Value first_value) { Field global = fs_h0.createField(option::name("global") | option::global(root) | options); Field local_h0 = fs_h0.createField(option::name("local_h0") | options); Field local_h2 = fs_h2.createField(option::name("local_h2") | options); if (mpi::comm().rank() == root) { fill_structured_field(global, first_value); } fs_h0.scatter(global, local_h0); fs_h2.scatter(global, local_h2); EXPECT_EQ(fs_h0.checksum(local_h0), fs_h2.checksum(local_h2)); }; check_field_checksum_stable_with_halo(option::variables(3) | option::levels(4), Value{18000}); check_field_checksum_stable_with_halo(option::variables(3), Value{19000}); check_field_checksum_stable_with_halo(option::levels(4), Value{20000}); check_field_checksum_stable_with_halo(util::Config{}, Value{21000}); auto check_fieldset_checksum_stable_with_halo = [&](const util::Config& options_a, const util::Config& options_b) { Field global_a = fs_h0.createField(option::name("global_a") | option::global(root) | options_a); Field global_b = fs_h0.createField(option::name("global_b") | option::global(root) | options_b); Field local_h0_a = fs_h0.createField(option::name("local_h0_a") | options_a); Field local_h0_b = fs_h0.createField(option::name("local_h0_b") | options_b); Field local_h2_a = fs_h2.createField(option::name("local_h2_a") | options_a); Field local_h2_b = fs_h2.createField(option::name("local_h2_b") | options_b); if (mpi::comm().rank() == root) { fill_structured_field(global_a, Value{22000}); fill_structured_field(global_b, Value{23000}); } fs_h0.scatter(global_a, local_h0_a); fs_h0.scatter(global_b, local_h0_b); fs_h2.scatter(global_a, local_h2_a); fs_h2.scatter(global_b, local_h2_b); FieldSet fieldset_h0; FieldSet fieldset_h2; fieldset_h0.add(local_h0_a); fieldset_h0.add(local_h0_b); fieldset_h2.add(local_h2_a); fieldset_h2.add(local_h2_b); EXPECT_EQ(fs_h0.checksum(fieldset_h0), fs_h2.checksum(fieldset_h2)); }; check_fieldset_checksum_stable_with_halo(/*a*/ option::variables(2) | option::levels(3), /*b*/ option::variables(4)); } //----------------------------------------------------------------------------- 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); } ecmwf-atlas-b0f79ee/src/tests/functionspace/CMakeLists.txt0000664000175000017500000001676415263665111024162 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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} ) add_fctest( TARGET atlas_fctest_blockstructuredcolumns_checksum MPI 2 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 2 LINKER_LANGUAGE Fortran SOURCES fctest_blockstructuredcolumns_checksum.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_spectral_functionspace MPI 4 CONDITION atlas_HAVE_ECTRANS AND eckit_HAVE_MPI AND ( transi_HAVE_MPI OR ectrans_HAVE_MPI ) AND MPI_SLOTS GREATER_EQUAL 4 SOURCES test_spectral_functionspace.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_spectral_functionspace_serial SOURCES test_spectral_functionspace.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_columns_checksum MPI 2 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 2 SOURCES test_columns_checksum.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_structuredcolumns_area SOURCES test_structuredcolumns_area.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) 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_blockstructuredcolumns_checksum SOURCES test_blockstructuredcolumns_checksum.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_blockstructuredcolumns_checksum_mpi COMMAND atlas_test_blockstructuredcolumns_checksum MPI 4 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 4 ) 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} ) ecmwf-atlas-b0f79ee/src/tests/functionspace/test_functionspace_locate.cc0000664000175000017500000000674115263665111027152 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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(fs.global_index()); if (functionspace::BlockStructuredColumns fsb{fs}) { auto f = array::make_view(field); auto g = array::make_view(fs.global_index()); for( idx_t jblk=0; jblk(field); for( idx_t j=0; j void check_scatter_metadata(const FunctionSpace& fs, Field& local, Field& global) { if (global.size()) { global.metadata().set("split_color", color()); } fs.scatter(global, local); EXPECT_EQUAL(local.metadata().get("split_color"), color()); } CASE("test FunctionSpace NodeColumns") { Fixture fixture; auto mesh = Mesh(grid(), option::mpi_split_comm()); auto fs = functionspace::NodeColumns(mesh,atlas::option::halo(1)); EXPECT_EQUAL(fs.part(),mpi::comm("split").rank()); EXPECT_EQUAL(fs.nb_parts(),mpi::comm("split").size()); EXPECT_EQUAL(fs.mpi_comm(),"split"); 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); }); } check_scatter_metadata(fs, field, fieldg); // Checksum auto checksum = fs.checksum(field); EXPECT_EQ(checksum, expected_checksum()); Log::error() << "fs.part() = " << fs.part() << " fs.nb_parts() = " << fs.nb_parts() << " grid = " << fs.grid().name() << " checksum = " << checksum << std::endl; // Output output::Gmsh gmsh(mesh.grid().name()+".msh"); gmsh.write(mesh); gmsh.write(field); } CASE("setMask initializes on a nonzero owner") { Fixture fixture; auto mesh = Mesh(grid(), option::mpi_split_comm()); FunctionSpace fs = functionspace::NodeColumns(mesh, atlas::option::halo(1)); constexpr idx_t root = 1; int initialization_count = 0; fs.setMask([&initialization_count](int* mask, size_t size) { ++initialization_count; for (size_t j = 0; j < size; ++j) { mask[j] = (j+1)%2; } }, root); const int expected_initialization_count = mpi::comm("split").rank() == root ? 1 : 0; EXPECT_EQUAL(initialization_count, expected_initialization_count); auto gidx = array::make_view(fs.global_index()); auto ghost = array::make_view(fs.ghost()); auto mask = array::make_view(fs.mask()); for (idx_t j = 0; j < fs.size(); ++j) { if (not ghost(j)) { EXPECT_EQUAL(mask(j), gidx[j]%2); } } } CASE("test FunctionSpace Spectral records communicator") { Fixture fixture; functionspace::Spectral spectral(3, util::Config("backend", "local") | util::Config("mpi_comm", "split")); EXPECT_EQUAL(spectral.mpi_comm(), "split"); EXPECT_EQUAL(spectral.part(), mpi::comm("split").rank()); EXPECT_EQUAL(spectral.nb_parts(), mpi::comm("split").size()); constexpr idx_t owner = 0; Field global = spectral.createField(atlas::option::global() | util::Config("owner", owner)); const bool is_owner = mpi::comm("split").rank() == owner; EXPECT_EQUAL(global.shape(0), (is_owner ? spectral.nb_spectral_coefficients_global() : 0)); idx_t iterations = 0; spectral.parallel_for(atlas::option::global() | util::Config("owner", owner), [&](idx_t, idx_t, int, int) { ++iterations; }); EXPECT_EQUAL(iterations, (is_owner ? spectral.nb_spectral_coefficients_global() / 2 : 0)); } 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); }); } check_scatter_metadata(fs, field, fieldg); // Checksum auto checksum = fs.checksum(field); EXPECT_EQ(checksum, expected_checksum()); Log::error() << "fs.part() = " << fs.part() << " fs.nb_parts() = " << fs.nb_parts() << " grid = " << fs.grid().name() << " checksum = " << checksum << std::endl; } 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()); Log::error() << "fs.part() = " << fs.part() << " fs.nb_parts() = " << fs.nb_parts() << " grid = " << fs.grid().name() << " checksum = " << checksum << std::endl; } //----------------------------------------------------------------------------- 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); }); } check_scatter_metadata(fs, field, fieldg); // Checksum // auto checksum = fs.checksum(field); // EXPECT_EQ(checksum, expected_checksum()); } //----------------------------------------------------------------------------- CASE("test FunctionSpace StructuredColumns with MatchingPartitioner") { Fixture fixture; auto g = grid(); auto fs_A = functionspace::StructuredColumns(g, option::mpi_split_comm()); auto fs_B = functionspace::StructuredColumns(g, 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); } ecmwf-atlas-b0f79ee/src/tests/functionspace/test_polygons.cc0000664000175000017500000001744515263665111024637 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/functionspace/fctest_blockstructuredcolumns.F900000664000175000017500000001536515263665111030066 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/functionspace/test_polygons_projection.cc0000664000175000017500000001516215263665111027065 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/functionspace/test_reduced_halo.cc0000664000175000017500000000610515263665111025372 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/functionspace/test_pointcloud_halo_creation.cc0000664000175000017500000001405415263665111030025 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" #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); } ecmwf-atlas-b0f79ee/src/tests/functionspace/test_structuredcolumns_biperiodic.cc0000664000175000017500000000656115263665111030760 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/functionspace/fctest_blockstructuredcolumns_checksum.F900000664000175000017500000006124215263665111031743 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 BlockStructuredColumns Checksum functionality. ! Tests verify that checksums are computed correctly for fields with various ! configurations, including multi-field fieldsets and fields wrapping slices of data. ! ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module fcta_BlockStructuredColumns_checksum_fxt use atlas_module use atlas_functionspace_blockstructuredcolumns_module use, intrinsic :: iso_c_binding implicit none character(len=1024) :: msg interface fill_field_with_data module procedure fill_field_with_data_field module procedure fill_field_with_data_real64_2d module procedure fill_field_with_data_real64_3d module procedure fill_field_with_data_real64_4d module procedure fill_field_with_data_real32_2d module procedure fill_field_with_data_real32_3d module procedure fill_field_with_data_real32_4d end interface contains ! Helper to fill a field deterministically based on functionspace global_index. ! This makes values reproducible for different MPI partitionings. subroutine fill_field_with_data_field(fs, field, seed) type(atlas_functionspace_BlockStructuredColumns), intent(in) :: fs type(atlas_Field), intent(inout) :: field integer, intent(in) :: seed real(c_double), pointer :: data_2d(:,:) real(c_double), pointer :: data_3d(:,:,:) real(c_float), pointer :: data_2d_f(:,:) real(c_float), pointer :: data_3d_f(:,:,:) if( field%rank() == 2 ) then if( field%kind() == atlas_real(c_double) ) then call field%data(data_2d) call fill_field_with_data_real64_2d(fs, data_2d, seed) else if( field%kind() == atlas_real(c_float) ) then call field%data(data_2d_f) call fill_field_with_data_real32_2d(fs, data_2d_f, seed) endif else if( field%rank() == 3 ) then if( field%kind() == atlas_real(c_double) ) then call field%data(data_3d) call fill_field_with_data_real64_3d(fs, data_3d, seed) else if( field%kind() == atlas_real(c_float) ) then call field%data(data_3d_f) call fill_field_with_data_real32_3d(fs, data_3d_f, seed) endif endif end subroutine fill_field_with_data_field subroutine fill_field_with_data_real64_2d(fs, data, seed) type(atlas_functionspace_BlockStructuredColumns), intent(in) :: fs real(c_double), intent(inout) :: data(:,:) integer, intent(in) :: seed type(atlas_Field) :: field_global_index integer(ATLAS_KIND_GIDX), pointer :: global_index(:) integer(ATLAS_KIND_IDX) :: jblk, jpt, i field_global_index = fs%global_index() call field_global_index%data(global_index) data = real(seed, c_double) do jblk = 1, fs%nblks() do i = 1, fs%block_size(jblk) jpt = fs%block_begin(jblk) + i - 1 data(i,jblk) = real(seed, c_double) + real(global_index(jpt), c_double) enddo enddo call field_global_index%final() end subroutine fill_field_with_data_real64_2d subroutine fill_field_with_data_real32_2d(fs, data, seed) type(atlas_functionspace_BlockStructuredColumns), intent(in) :: fs real(c_float), intent(inout) :: data(:,:) integer, intent(in) :: seed type(atlas_Field) :: field_global_index integer(ATLAS_KIND_GIDX), pointer :: global_index(:) integer(ATLAS_KIND_IDX) :: jblk, jpt, i field_global_index = fs%global_index() call field_global_index%data(global_index) data = real(seed, c_float) do jblk = 1, fs%nblks() do i = 1, fs%block_size(jblk) jpt = fs%block_begin(jblk) + i - 1 data(i,jblk) = real(seed, c_float) + real(global_index(jpt), c_float) enddo enddo call field_global_index%final() end subroutine fill_field_with_data_real32_2d subroutine fill_field_with_data_real64_3d(fs, data, seed) type(atlas_functionspace_BlockStructuredColumns), intent(in) :: fs real(c_double), intent(inout) :: data(:,:,:) integer, intent(in) :: seed type(atlas_Field) :: field_global_index integer(ATLAS_KIND_GIDX), pointer :: global_index(:) integer(ATLAS_KIND_IDX) :: jblk, jpt, i integer :: j field_global_index = fs%global_index() call field_global_index%data(global_index) data = real(seed, c_double) do jblk = 1, fs%nblks() do i = 1, fs%block_size(jblk) jpt = fs%block_begin(jblk) + i - 1 do j = 1, size(data,2) data(i,j,jblk) = real(seed, c_double) + real(global_index(jpt), c_double) + real(j, c_double) enddo enddo enddo call field_global_index%final() end subroutine fill_field_with_data_real64_3d subroutine fill_field_with_data_real32_3d(fs, data, seed) type(atlas_functionspace_BlockStructuredColumns), intent(in) :: fs real(c_float), intent(inout) :: data(:,:,:) integer, intent(in) :: seed type(atlas_Field) :: field_global_index integer(ATLAS_KIND_GIDX), pointer :: global_index(:) integer(ATLAS_KIND_IDX) :: jblk, jpt, i integer :: j field_global_index = fs%global_index() call field_global_index%data(global_index) data = real(seed, c_float) do jblk = 1, fs%nblks() do i = 1, fs%block_size(jblk) jpt = fs%block_begin(jblk) + i - 1 do j = 1, size(data,2) data(i,j,jblk) = real(seed, c_float) + real(global_index(jpt), c_float) + real(j, c_float) enddo enddo enddo call field_global_index%final() end subroutine fill_field_with_data_real32_3d subroutine fill_field_with_data_real64_4d(fs, data, seed) type(atlas_functionspace_BlockStructuredColumns), intent(in) :: fs real(c_double), intent(inout) :: data(:,:,:,:) integer, intent(in) :: seed type(atlas_Field) :: field_global_index integer(ATLAS_KIND_GIDX), pointer :: global_index(:) integer(ATLAS_KIND_IDX) :: jblk, jpt, i integer :: j, k field_global_index = fs%global_index() call field_global_index%data(global_index) data = real(seed, c_double) do jblk = 1, fs%nblks() do i = 1, fs%block_size(jblk) jpt = fs%block_begin(jblk) + i - 1 do j = 1, size(data,2) do k = 1, size(data,3) data(i,j,k,jblk) = real(seed, c_double) + real(global_index(jpt), c_double) + real(j, c_double) + real(k, c_double) enddo enddo enddo enddo call field_global_index%final() end subroutine fill_field_with_data_real64_4d subroutine fill_field_with_data_real32_4d(fs, data, seed) type(atlas_functionspace_BlockStructuredColumns), intent(in) :: fs real(c_float), intent(inout) :: data(:,:,:,:) integer, intent(in) :: seed type(atlas_Field) :: field_global_index integer(ATLAS_KIND_GIDX), pointer :: global_index(:) integer(ATLAS_KIND_IDX) :: jblk, jpt, i integer :: j, k field_global_index = fs%global_index() call field_global_index%data(global_index) data = real(seed, c_float) do jblk = 1, fs%nblks() do i = 1, fs%block_size(jblk) jpt = fs%block_begin(jblk) + i - 1 do j = 1, size(data,2) do k = 1, size(data,3) data(i,j,k,jblk) = real(seed, c_float) + real(global_index(jpt), c_float) + real(j, c_float) + real(k, c_float) enddo enddo enddo enddo call field_global_index%final() end subroutine fill_field_with_data_real32_4d end module ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fcta_BlockStructuredColumns_checksum, fcta_BlockStructuredColumns_checksum_fxt) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ============================================================================ ! Test: Checksum for single field (double precision, no levels) ! ============================================================================ TEST( test_blockstructuredcolumns_checksum_single_field_2d ) use atlas_functionspace_blockstructuredcolumns_module use fckit_mpi_module implicit none type(atlas_StructuredGrid) :: grid type(atlas_functionspace_BlockStructuredColumns) :: fs type(atlas_Field) :: field1, field2 character(len=256) :: checksum1, checksum2 type(fckit_mpi_comm) :: mpi mpi = fckit_mpi_comm() grid = atlas_StructuredGrid("O8") fs = atlas_functionspace_BlockStructuredColumns(grid, halo=0, nproma=4) ! Create two identical fields and verify checksums are equal field1 = fs%create_field(name="field1", kind=atlas_real(c_double)) field2 = fs%create_field(name="field2", kind=atlas_real(c_double)) call fill_field_with_data(fs, field1, 42) call fill_field_with_data(fs, field2, 42) checksum1 = fs%checksum(field1) checksum2 = fs%checksum(field2) if( mpi%rank() == 0 ) then write(msg,*) "Checksum 1: ", trim(checksum1) call atlas_log%info(msg) write(msg,*) "Checksum 2: ", trim(checksum2) call atlas_log%info(msg) endif FCTEST_CHECK_EQUAL( checksum1, checksum2 ) call field1%final() call field2%final() call fs%final() call grid%final() END_TEST ! ============================================================================ ! Test: Checksum for single field (float precision, with levels) ! ============================================================================ TEST( test_blockstructuredcolumns_checksum_3d_field ) use atlas_functionspace_blockstructuredcolumns_module use fckit_mpi_module implicit none type(atlas_StructuredGrid) :: grid type(atlas_functionspace_BlockStructuredColumns) :: fs type(atlas_Field) :: field character(len=256) :: checksum1, checksum2 type(fckit_mpi_comm) :: mpi mpi = fckit_mpi_comm() grid = atlas_StructuredGrid("O8") fs = atlas_functionspace_BlockStructuredColumns(grid, halo=0, nproma=4, levels=5) field = fs%create_field(name="field", kind=atlas_real(c_float)) call fill_field_with_data(fs, field, 123) ! First checksum checksum1 = fs%checksum(field) ! Recompute should give same checksum checksum2 = fs%checksum(field) if( mpi%rank() == 0 ) then write(msg,*) "3D Field Checksum 1: ", trim(checksum1) call atlas_log%info(msg) write(msg,*) "3D Field Checksum 2: ", trim(checksum2) call atlas_log%info(msg) endif FCTEST_CHECK_EQUAL( checksum1, checksum2 ) call field%final() call fs%final() call grid%final() END_TEST ! ============================================================================ ! Test: Checksum for fieldset with multiple fields ! ============================================================================ TEST( test_blockstructuredcolumns_checksum_fieldset_multiple_fields ) use atlas_functionspace_blockstructuredcolumns_module use fckit_mpi_module implicit none type(atlas_StructuredGrid) :: grid type(atlas_functionspace_BlockStructuredColumns) :: fs type(atlas_Field) :: field1, field2, field3 type(atlas_FieldSet) :: fieldset, fieldset2 character(len=256) :: checksum1, checksum2, checksum_field1 type(fckit_mpi_comm) :: mpi mpi = fckit_mpi_comm() grid = atlas_StructuredGrid("O8") fs = atlas_functionspace_BlockStructuredColumns(grid, halo=0, nproma=4) ! Create three fields with different data field1 = fs%create_field(name="field1", kind=atlas_real(c_double)) field2 = fs%create_field(name="field2", kind=atlas_real(c_double)) field3 = fs%create_field(name="field3", kind=atlas_real(c_float)) call fill_field_with_data(fs, field1, 100) call fill_field_with_data(fs, field2, 200) call fill_field_with_data(fs, field3, 300) ! Create fieldsets with same fields in same order fieldset = atlas_FieldSet() call fieldset%add(field1) call fieldset%add(field2) call fieldset%add(field3) fieldset2 = atlas_FieldSet() call fieldset2%add(field1) call fieldset2%add(field2) call fieldset2%add(field3) ! Checksums of identical fieldsets should match checksum1 = fs%checksum(fieldset) checksum2 = fs%checksum(fieldset2) if( mpi%rank() == 0 ) then write(msg,*) "FieldSet Checksum 1: ", trim(checksum1) call atlas_log%info(msg) write(msg,*) "FieldSet Checksum 2: ", trim(checksum2) call atlas_log%info(msg) endif FCTEST_CHECK_EQUAL( checksum1, checksum2 ) ! Also verify that individual field checksum differs from combined fieldset checksum_field1 = fs%checksum(field1) if( mpi%rank() == 0 ) then write(msg,*) "Field 1 Checksum: ", trim(checksum_field1) call atlas_log%info(msg) write(msg,*) "FieldSet contains Field 1 but they have different checksums" call atlas_log%info(msg) endif ! FieldSet checksum should differ from single field checksum (contains 3 fields) FCTEST_CHECK( checksum1 /= checksum_field1 ) call fieldset%final() call fieldset2%final() call field1%final() call field2%final() call field3%final() call fs%final() call grid%final() END_TEST ! ============================================================================ ! Test: Checksum determinism with multiple variables per field ! ============================================================================ TEST( test_blockstructuredcolumns_checksum_multiple_variables ) use atlas_functionspace_blockstructuredcolumns_module use fckit_mpi_module implicit none type(atlas_StructuredGrid) :: grid type(atlas_functionspace_BlockStructuredColumns) :: fs type(atlas_Field) :: field1, field2 character(len=256) :: checksum1, checksum2 type(fckit_mpi_comm) :: mpi mpi = fckit_mpi_comm() grid = atlas_StructuredGrid("O8") fs = atlas_functionspace_BlockStructuredColumns(grid, halo=0, nproma=4) ! Create fields with multiple variables field1 = fs%create_field(name="multi_var1", kind=atlas_real(c_double), variables=3) field2 = fs%create_field(name="multi_var2", kind=atlas_real(c_double), variables=3) call fill_field_with_data(fs, field1, 555) call fill_field_with_data(fs, field2, 555) checksum1 = fs%checksum(field1) checksum2 = fs%checksum(field2) if( mpi%rank() == 0 ) then write(msg,*) "Multi-var Checksum 1: ", trim(checksum1) call atlas_log%info(msg) write(msg,*) "Multi-var Checksum 2: ", trim(checksum2) call atlas_log%info(msg) endif FCTEST_CHECK_EQUAL( checksum1, checksum2 ) call field1%final() call field2%final() call fs%final() call grid%final() END_TEST ! ============================================================================ ! Test: Checksum invariance with halo - should exclude halo region ! Note: This test skipped because properly testing halo exclusion requires ! more careful setup of owned vs halo regions. The C++ tests verify this. ! ============================================================================ ! TEST( test_blockstructuredcolumns_checksum_halo_consistency ) ! SKIPPED - See C++ tests for halo invariance verification ! ! ============================================================================ ! Test: Checksum differs when data changes ! ============================================================================ TEST( test_blockstructuredcolumns_checksum_data_sensitivity ) use atlas_functionspace_blockstructuredcolumns_module use fckit_mpi_module implicit none type(atlas_StructuredGrid) :: grid type(atlas_functionspace_BlockStructuredColumns) :: fs type(atlas_Field) :: field character(len=256) :: checksum1, checksum2 real(c_double), pointer :: data_2d(:,:) type(fckit_mpi_comm) :: mpi mpi = fckit_mpi_comm() grid = atlas_StructuredGrid("O8") fs = atlas_functionspace_BlockStructuredColumns(grid, halo=0, nproma=4) field = fs%create_field(name="field", kind=atlas_real(c_double)) ! Fill with initial data call fill_field_with_data(fs, field, 42) checksum1 = fs%checksum(field) ! Modify data call field%data(data_2d) data_2d = data_2d + 1.0d0 checksum2 = fs%checksum(field) if( mpi%rank() == 0 ) then write(msg,*) "Checksum before modification: ", trim(checksum1) call atlas_log%info(msg) write(msg,*) "Checksum after modification: ", trim(checksum2) call atlas_log%info(msg) endif ! Checksums should differ after data modification FCTEST_CHECK( checksum1 /= checksum2 ) call field%final() call fs%final() call grid%final() END_TEST ! ============================================================================ ! Test: Checksum determinism for different data types ! ============================================================================ TEST( test_blockstructuredcolumns_checksum_data_type_sensitivity ) use atlas_functionspace_blockstructuredcolumns_module use fckit_mpi_module implicit none type(atlas_StructuredGrid) :: grid type(atlas_functionspace_BlockStructuredColumns) :: fs type(atlas_Field) :: field_double, field_float, field1, field2 real(c_double), pointer :: data_dbl(:,:), data_dbl2(:,:) real(c_float), pointer :: data_flt(:,:) character(len=256) :: checksum_double, checksum_float, checksum1, checksum2 type(fckit_mpi_comm) :: mpi mpi = fckit_mpi_comm() grid = atlas_StructuredGrid("O16") fs = atlas_functionspace_BlockStructuredColumns(grid, halo=0, nproma=8) ! ======================================================================== ! Test 1: Fields created through functionspace, data wraps internal buffers ! (This demonstrates how BlockStructuredColumns manages field data internally) ! ======================================================================== field_double = fs%create_field(name="field_double", kind=atlas_real(c_double)) field_float = fs%create_field(name="field_float", kind=atlas_real(c_float)) ! Access and fill the wrapped data call field_double%data(data_dbl) call field_float%data(data_flt) ! Fill with deterministic values data_dbl = 1.618033988749895d0 data_flt = real(1.618033988749895d0, c_float) ! Compute checksums checksum_double = fs%checksum(field_double) checksum_float = fs%checksum(field_float) if( mpi%rank() == 0 ) then write(msg,*) "Field wrapping double precision data: ", trim(checksum_double) call atlas_log%info(msg) write(msg,*) "Field wrapping single precision data: ", trim(checksum_float) call atlas_log%info(msg) write(msg,*) "Different data types produce different checksums" call atlas_log%info(msg) endif ! Different data types should have different checksums FCTEST_CHECK( checksum_double /= checksum_float ) ! ======================================================================== ! Test 2: Demonstrates that modifying the data changes checksum ! ======================================================================== field1 = fs%create_field(name="field1", kind=atlas_real(c_double)) field2 = fs%create_field(name="field2", kind=atlas_real(c_double)) call field1%data(data_dbl) call field2%data(data_dbl2) ! Fill both with same data data_dbl = 3.14159265358979d0 data_dbl2 = 3.14159265358979d0 checksum1 = fs%checksum(field1) checksum2 = fs%checksum(field2) if( mpi%rank() == 0 ) then write(msg,*) "Field 1 checksum (same data): ", trim(checksum1) call atlas_log%info(msg) write(msg,*) "Field 2 checksum (same data): ", trim(checksum2) call atlas_log%info(msg) endif FCTEST_CHECK_EQUAL( checksum1, checksum2 ) ! Modify wrapped data in field1 data_dbl = 2.71828182845904d0 checksum1 = fs%checksum(field1) if( mpi%rank() == 0 ) then write(msg,*) "Field 1 checksum (modified data): ", trim(checksum1) call atlas_log%info(msg) write(msg,*) "Checksum changed because wrapped data was modified" call atlas_log%info(msg) endif ! Checksums should differ after modification FCTEST_CHECK( checksum1 /= checksum2 ) call field_double%final() call field_float%final() call field1%final() call field2%final() call fs%final() call grid%final() END_TEST ! ============================================================================ ! Test: Checksum consistency across rank types ! ============================================================================ TEST( test_blockstructuredcolumns_checksum_rank_consistency ) use atlas_functionspace_blockstructuredcolumns_module use fckit_mpi_module implicit none type(atlas_StructuredGrid) :: grid type(atlas_functionspace_BlockStructuredColumns) :: fs type(atlas_Field) :: field_rank2, field_rank3, field_rank4 character(len=256) :: checksum_r2, checksum_r3, checksum_r4 type(fckit_mpi_comm) :: mpi mpi = fckit_mpi_comm() grid = atlas_StructuredGrid("O8") fs = atlas_functionspace_BlockStructuredColumns(grid, halo=0, nproma=4, levels=5) ! Create fields of different ranks field_rank2 = fs%create_field(name="rank2_field", kind=atlas_real(c_double)) field_rank3 = fs%create_field(name="rank3_field", kind=atlas_real(c_double), levels=5) field_rank4 = fs%create_field(name="rank4_field", kind=atlas_real(c_double), variables=2, levels=5) call fill_field_with_data(fs, field_rank2, 111) call fill_field_with_data(fs, field_rank3, 222) call fill_field_with_data(fs, field_rank4, 333) ! Compute checksums for different rank fields checksum_r2 = fs%checksum(field_rank2) checksum_r3 = fs%checksum(field_rank3) checksum_r4 = fs%checksum(field_rank4) if( mpi%rank() == 0 ) then write(msg,*) "Rank 2 Field Checksum: ", trim(checksum_r2) call atlas_log%info(msg) write(msg,*) "Rank 3 Field Checksum: ", trim(checksum_r3) call atlas_log%info(msg) write(msg,*) "Rank 4 Field Checksum: ", trim(checksum_r4) call atlas_log%info(msg) write(msg,*) "All checksums should be different (different data and shapes)" call atlas_log%info(msg) endif ! Checksums should be different (different data) FCTEST_CHECK( checksum_r2 /= checksum_r3 ) FCTEST_CHECK( checksum_r3 /= checksum_r4 ) FCTEST_CHECK( checksum_r2 /= checksum_r4 ) call field_rank2%final() call field_rank3%final() call field_rank4%final() call fs%final() call grid%final() END_TEST TEST( test_blockstructuredcolumns_wrapped_fields ) type(atlas_StructuredGrid) :: grid type(atlas_functionspace_BlockStructuredColumns) :: fs type(atlas_Field) :: field_1, field_2, field_3, field_gfl type(atlas_FieldSet) :: fieldset real(c_double), allocatable :: gfl(:,:,:,:) character(len=256) :: checksum integer(c_int) :: nlev = 10, nproma = 8, nvar = 3 grid = atlas_StructuredGrid("O32") fs = atlas_functionspace_BlockStructuredColumns(grid, nproma=nproma) allocate(gfl(fs%nproma(),nlev,nvar,fs%nblks())) call fill_field_with_data(fs, gfl, 42) field_1 = atlas_Field(name="field_1", data=gfl(:,:,1,:)) checksum = fs%checksum(field_1) ! Should compute checksum without error on wrapped data write(msg,*) "Checksum for field_1 wrapping 3D slice: ", trim(checksum) call atlas_log%info(msg) FCTEST_CHECK_EQUAL(checksum, "8b78") field_2 = atlas_Field(name="field_2", data=gfl(:,:,2,:)) checksum = fs%checksum(field_2) ! Should compute checksum without error on wrapped data write(msg,*) "Checksum for field_2 wrapping 3D slice: ", trim(checksum) call atlas_log%info(msg) FCTEST_CHECK_EQUAL(checksum, "31a0") field_3 = atlas_Field(name="field_3", data=gfl(:,:,3,:)) checksum = fs%checksum(field_3) ! Should compute checksum without error on wrapped data write(msg,*) "Checksum for field_3 wrapping 3D slice: ", trim(checksum) call atlas_log%info(msg) FCTEST_CHECK_EQUAL(checksum, "5496") fieldset = atlas_FieldSet() call fieldset%add(field_1) call fieldset%add(field_2) call fieldset%add(field_3) checksum = fs%checksum(fieldset) ! Should compute checksum without error on fieldset of wrapped fields write(msg,*) "Checksum for fieldset of 3 wrapped fields: ", trim(checksum) call atlas_log%info(msg) FCTEST_CHECK_EQUAL(checksum, "9305") field_gfl = atlas_Field(name="gfl", data=gfl(:,:,:,:)) checksum = fs%checksum(field_gfl) ! Should compute checksum without error on wrapped data write(msg,*) "Checksum for field_gfl wrapping 4D array: ", trim(checksum) call atlas_log%info(msg) FCTEST_CHECK_EQUAL(checksum, "338c") END_TEST TEST( test_blockstructuredcolumns_checksum_array_api ) type(atlas_StructuredGrid) :: grid type(atlas_functionspace_BlockStructuredColumns) :: fs real(c_double), allocatable :: gfl(:,:,:,:) character(len=256) :: checksum integer(c_int) :: nlev = 10, nproma = 8, nvar = 3 grid = atlas_StructuredGrid("O32") fs = atlas_functionspace_BlockStructuredColumns(grid, nproma=nproma) allocate(gfl(fs%nproma(),nlev,nvar,fs%nblks())) call fill_field_with_data(fs, gfl, 42) checksum = fs%checksum(gfl(:,:,1,:)) write(msg,*) "Checksum for array API wrapping 3D slice 1: ", trim(checksum) call atlas_log%info(msg) FCTEST_CHECK_EQUAL(checksum, "8b78") checksum = fs%checksum(gfl(:,:,2,:)) write(msg,*) "Checksum for array API wrapping 3D slice 2: ", trim(checksum) call atlas_log%info(msg) FCTEST_CHECK_EQUAL(checksum, "31a0") checksum = fs%checksum(gfl(:,:,3,:)) write(msg,*) "Checksum for array API wrapping 3D slice 3: ", trim(checksum) call atlas_log%info(msg) FCTEST_CHECK_EQUAL(checksum, "5496") checksum = fs%checksum(gfl(:,:,:,:)) write(msg,*) "Checksum for array API wrapping 4D array: ", trim(checksum) call atlas_log%info(msg) FCTEST_CHECK_EQUAL(checksum, "338c") call fs%final() call grid%final() END_TEST END_TESTSUITE ecmwf-atlas-b0f79ee/src/tests/functionspace/test_blockstructuredcolumns.cc0000664000175000017500000001525315263665111027600 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/functionspace/test_stencil.cc0000664000175000017500000002234415263665111024420 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/functionspace/test_columns_checksum.cc0000664000175000017500000002211315263665111026313 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/MakeView.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/CellColumns.h" #include "atlas/functionspace/EdgeColumns.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/grid.h" #include "atlas/mesh.h" #include "atlas/meshgenerator.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/Checksum.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas; using namespace atlas::functionspace; using namespace atlas::test; namespace { template Value point_value(gidx_t global_index, idx_t dim1 = 0, idx_t dim2 = 0) { return static_cast(global_index * 100 + dim1 * 10 + dim2 + 1); } template void fill_owned_and_ghost_values(const FunctionSpace& fs, Field& field, Value ghost_value) { auto global_index = array::make_view(fs.global_index()); auto ghost = array::make_view(fs.ghost()); switch (field.rank()) { case 1: { auto view = array::make_view(field); for (idx_t point = 0; point < field.shape(0); ++point) { view(point) = ghost(point) ? ghost_value : point_value(global_index(point)); } break; } case 2: { auto view = array::make_view(field); for (idx_t point = 0; point < field.shape(0); ++point) { for (idx_t dim1 = 0; dim1 < field.shape(1); ++dim1) { view(point, dim1) = ghost(point) ? ghost_value : point_value(global_index(point), dim1); } } break; } case 3: { auto view = array::make_view(field); for (idx_t point = 0; point < field.shape(0); ++point) { for (idx_t dim1 = 0; dim1 < field.shape(1); ++dim1) { for (idx_t dim2 = 0; dim2 < field.shape(2); ++dim2) { view(point, dim1, dim2) = ghost(point) ? ghost_value : point_value(global_index(point), dim1, dim2); } } } break; } default: ATLAS_NOTIMPLEMENTED; } field.set_dirty(); } template void update_lane_states_with_global_field(const Field& field, std::vector& lane_states) { ATLAS_ASSERT(field.shape(0) == static_cast(lane_states.size())); switch (field.rank()) { case 1: { auto view = array::make_view(field); for (idx_t point = 0; point < field.shape(0); ++point) { util::checksum_update(lane_states[static_cast(point)], &view(point), 1); } break; } case 2: { auto view = array::make_view(field); for (idx_t point = 0; point < field.shape(0); ++point) { util::checksum_update(lane_states[static_cast(point)], &view(point, 0), static_cast(field.shape(1))); } break; } case 3: { auto view = array::make_view(field); for (idx_t point = 0; point < field.shape(0); ++point) { util::checksum_update(lane_states[static_cast(point)], &view(point, 0, 0), static_cast(field.shape(1) * field.shape(2))); } break; } default: ATLAS_NOTIMPLEMENTED; } } util::checksum_t checksum_from_lane_states(const std::vector& lane_states) { std::vector lane_digests(lane_states.size()); for (size_t point = 0; point < lane_states.size(); ++point) { lane_digests[point] = util::checksum_digest(lane_states[point]); } return util::checksum(lane_digests.data(), lane_digests.size()); } template std::string expected_field_checksum(const Field& global_field, idx_t root = 0) { util::checksum_t expected_checksum = 0; auto& comm = mpi::comm(); if (comm.rank() == root) { std::vector lane_states(static_cast(global_field.shape(0))); for (auto& lane_state : lane_states) { util::checksum_reset(lane_state); } update_lane_states_with_global_field(global_field, lane_states); expected_checksum = checksum_from_lane_states(lane_states); } comm.broadcast(expected_checksum, root); return util::checksum_to_hex_str(expected_checksum); } template std::string expected_fieldset_checksum(const FieldSet& global_fields, idx_t root = 0) { util::checksum_t expected_checksum = 0; auto& comm = mpi::comm(); if (comm.rank() == root) { ATLAS_ASSERT(global_fields.size() > 0); std::vector lane_states(static_cast(global_fields[0].shape(0))); for (auto& lane_state : lane_states) { util::checksum_reset(lane_state); } for (idx_t field_idx = 0; field_idx < global_fields.size(); ++field_idx) { update_lane_states_with_global_field(global_fields[field_idx], lane_states); } expected_checksum = checksum_from_lane_states(lane_states); } comm.broadcast(expected_checksum, root); return util::checksum_to_hex_str(expected_checksum); } template Field gather_global_field(const FunctionSpace& fs, const Field& local_field, idx_t root = 0) { Field global_field = fs.createField(local_field, option::global(root)); fs.gather(local_field, global_field); return global_field; } template FieldSet gather_global_fieldset(const FunctionSpace& fs, const FieldSet& local_fields, idx_t root = 0) { FieldSet global_fields; for (idx_t field_idx = 0; field_idx < local_fields.size(); ++field_idx) { global_fields.add(fs.createField(local_fields[field_idx], option::global(root))); } fs.gather(local_fields, global_fields); return global_fields; } Mesh generate_mesh() { Grid grid{"O16"}; return StructuredMeshGenerator().generate(grid); } template void expect_checksum_matches_global_field(const char* expected_checksum) { auto levels = 3; FunctionSpace fs(generate_mesh(), option::halo(1) | option::levels(levels)); Field field = fs.template createField(option::name("field") | option::variables(2)); auto first_ghost_value = -999.; fill_owned_and_ghost_values(fs, field, first_ghost_value); Field global_field = gather_global_field(fs, field); const auto checksum = expected_field_checksum(global_field); EXPECT_EQ(fs.checksum(field), checksum); auto second_ghost_value = -12345.; fill_owned_and_ghost_values(fs, field, second_ghost_value); EXPECT_EQ(fs.checksum(field), checksum); EXPECT_EQ(checksum, expected_checksum); } template void expect_checksum_matches_global_fieldset(const char* expected_checksum) { auto levels = 2; FunctionSpace fs(generate_mesh(), option::halo(1) | option::levels(levels)); Field field1 = fs.template createField(option::name("field1")); Field field2 = fs.template createField(option::name("field2") | option::variables(2)); fill_owned_and_ghost_values(fs, field1, -1.); fill_owned_and_ghost_values(fs, field2, -2.); FieldSet fields; fields.add(field1); fields.add(field2); FieldSet global_fields = gather_global_fieldset(fs, fields); const auto checksum = expected_fieldset_checksum(global_fields); EXPECT_EQ(fs.checksum(fields), checksum); EXPECT_EQ(checksum, expected_checksum); } } // namespace namespace atlas { namespace test { CASE("test_nodecolumns_checksum_matches_global_field") { expect_checksum_matches_global_field("7290"); } CASE("test_nodecolumns_checksum_fieldset_matches_global_fieldset") { expect_checksum_matches_global_fieldset("b0c2"); } CASE("test_cellcolumns_checksum_matches_global_field") { expect_checksum_matches_global_field("f645"); } CASE("test_cellcolumns_checksum_fieldset_matches_global_fieldset") { expect_checksum_matches_global_fieldset("893a"); } CASE("test_edgecolumns_checksum_matches_global_field") { expect_checksum_matches_global_field("7098"); } CASE("test_edgecolumns_checksum_fieldset_matches_global_fieldset") { expect_checksum_matches_global_fieldset("263b"); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); }ecmwf-atlas-b0f79ee/src/tests/functionspace/test_functionspace.cc0000664000175000017500000010444015263665111025616 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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("NodeColumns owns its mask") { Mesh mesh = StructuredMeshGenerator().generate(Grid("O8")); functionspace::NodeColumns first(mesh); functionspace::NodeColumns second(mesh); auto first_mask = array::make_view(first.mask()); auto second_mask = array::make_view(second.mask()); first_mask(0) = 0; EXPECT(second_mask(0) == 1); EXPECT(!mesh.nodes().has_field("mask")); } 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()); } { Log::info() << "Testing that NodeColumns statistics exclude ghost nodes" << std::endl; const auto ghost = array::make_view(mesh.nodes().ghost()); double minimum; double maximum; double mean; double stddev; idx_t count; gidx_t global_index; surface_scalar.assign(2.); for (idx_t node = 0; node < nodes_fs.nb_nodes(); ++node) { if (ghost(node)) { surface_scalar(node) = -100.; } } nodes_fs.minimum(surface_scalar_field, minimum); EXPECT(minimum == 2.); nodes_fs.minimumAndLocation(surface_scalar_field, minimum, global_index); EXPECT(minimum == 2.); for (idx_t node = 0; node < nodes_fs.nb_nodes(); ++node) { if (ghost(node)) { surface_scalar(node) = 100.; } } nodes_fs.maximum(surface_scalar_field, maximum); EXPECT(maximum == 2.); nodes_fs.maximumAndLocation(surface_scalar_field, maximum, global_index); EXPECT(maximum == 2.); nodes_fs.meanAndStandardDeviation(surface_scalar_field, mean, stddev, count); EXPECT(mean == 2.); EXPECT(stddev == 0.); Field min_per_level("min", array::make_datatype(), array::make_shape(nb_levels)); Field max_per_level("max", 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)); columns_scalar.assign(2.); for (idx_t node = 0; node < nodes_fs.nb_nodes(); ++node) { if (ghost(node)) { for (idx_t level = 0; level < nb_levels; ++level) { columns_scalar(node, level) = -100.; } } } nodes_fs.minimumPerLevel(columns_scalar_field, min_per_level); nodes_fs.minimumAndLocationPerLevel(columns_scalar_field, min_per_level, gidx_per_level); auto minimum_levels = array::make_view(min_per_level); for (idx_t level = 0; level < nb_levels; ++level) { EXPECT(minimum_levels(level) == 2.); } for (idx_t node = 0; node < nodes_fs.nb_nodes(); ++node) { if (ghost(node)) { for (idx_t level = 0; level < nb_levels; ++level) { columns_scalar(node, level) = 100.; } } } nodes_fs.maximumPerLevel(columns_scalar_field, max_per_level); nodes_fs.maximumAndLocationPerLevel(columns_scalar_field, max_per_level, gidx_per_level); auto maximum_levels = array::make_view(max_per_level); nodes_fs.meanAndStandardDeviationPerLevel(columns_scalar_field, mean_per_level, stddev_per_level, count); auto mean_levels = array::make_view(mean_per_level); auto stddev_levels = array::make_view(stddev_per_level); for (idx_t level = 0; level < nb_levels; ++level) { EXPECT(maximum_levels(level) == 2.); EXPECT(mean_levels(level) == 2.); EXPECT(stddev_levels(level) == 0.); } } 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); } CASE("test_createField_copy_preserves_name") { auto expect_copy_preserves_name = [](const auto& fs, const std::string& name, const auto& options) { Field source = fs.template createField(option::name(name) | options); Field copy = fs.createField(source); EXPECT(copy.name() == name); }; SECTION("CellColumns") { Mesh mesh = StructuredMeshGenerator().generate(Grid("O8")); CellColumns cell_fs(mesh); expect_copy_preserves_name(cell_fs, "cell-source", option::levels(2) | option::variables(3)); } SECTION("EdgeColumns") { Mesh mesh = StructuredMeshGenerator().generate(Grid("O8")); EdgeColumns edge_fs(mesh); expect_copy_preserves_name(edge_fs, "edge-source", option::levels(2) | option::variables(3)); } SECTION("NodeColumns") { Mesh mesh = StructuredMeshGenerator().generate(Grid("O8")); NodeColumns node_fs(mesh); expect_copy_preserves_name(node_fs, "node-source", option::levels(2) | option::variables(3)); } SECTION("PointCloud") { Grid grid("O8"); PointCloud pointcloud_fs(grid); expect_copy_preserves_name(pointcloud_fs, "pointcloud-source", option::levels(2) | option::variables(3)); } SECTION("StructuredColumns") { StructuredGrid grid("O8"); StructuredColumns structured_fs(grid, option::levels(2)); expect_copy_preserves_name(structured_fs, "structured-source", option::levels(2) | option::variables(3)); } SECTION("BlockStructuredColumns") { StructuredGrid grid("O8"); BlockStructuredColumns blockstructured_fs(grid, option::levels(2) | option::nproma(4)); expect_copy_preserves_name(blockstructured_fs, "blockstructured-source", option::levels(2) | option::variables(3)); } SECTION("Spectral") { Spectral spectral_fs(159); expect_copy_preserves_name(spectral_fs, "spectral-source", option::levels(2)); } } #if ATLAS_HAVE_TRANS CASE("test_SpectralFunctionSpace_trans_dist") { trans::Trans trans(Grid("F80"), 159); idx_t nb_levels(10); Spectral spectral_fs = trans.spectral(); 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.spectral(); 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 } } } #endif 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") { if (not ATLAS_HAVE_TESSELATION) { Log::info() << "Skipping test_functionspace_grid since tessellation is not available" << std::endl; return; } // 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()); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } ecmwf-atlas-b0f79ee/src/tests/functionspace/test_pointcloud.cc0000664000175000017500000002340115263665111025132 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/grid/Distribution.h" #include "atlas/grid/Grid.h" #include "atlas/grid/detail/grid/Unstructured.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 { //----------------------------------------------------------------------------- // Models grids such as ORCA whose stored points already include intrinsic ghosts // at the South boundary which don't participate in halo exchanges. // Points 0 and 7 are duplicates mapped to masters 4 and 3, while point 6 is an // intrinsic ghost that maps to itself and therefore remains a PointCloud source point. class GridWithExtensionAndDuplicates : public grid::detail::grid::Unstructured { public: GridWithExtensionAndDuplicates(): Unstructured({{40., 0.}, {10., 0.}, {20., 0.}, {30., 0.}, {40., 0.}, {50., 0.}, {60., 0.}, {30., 0.}}) {} int flags(gidx_t index) const override { GridPointFlags flags = index == 0 || index == 6 || index == 7 ? GridPointFlags::extension : GridPointFlags::none; if (index == 0 || index == 7) { flags.set(GridPointFlags::duplicate); } return flags.value(); } gidx_t masterIndex(gidx_t index) const override { return index == 0 ? 4 : index == 7 ? 3 : index; } }; //----------------------------------------------------------------------------- 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_functionspace_PointCloud with intrinsic grid ghosts") { EXPECT_EQ(mpi::size(), 2); Grid grid(new GridWithExtensionAndDuplicates()); int partitions[] = {0, 0, 0, 0, 1, 1, 1, 1}; grid::Distribution distribution(2, grid.size(), partitions); functionspace::PointCloud pointcloud(grid, distribution); auto ghost = array::make_view(pointcloud.ghost()); auto global_index = array::make_view(pointcloud.global_index()); auto remote_index = array::make_indexview(pointcloud.remote_index()); EXPECT_EQ(pointcloud.size(), 4); if (mpi::rank() == 0) { EXPECT(ghost(0)); EXPECT(!ghost(1)); EXPECT(!ghost(2)); EXPECT(!ghost(3)); EXPECT_EQ(remote_index(0), 0); } else { EXPECT(!ghost(0)); EXPECT(!ghost(1)); EXPECT(!ghost(2)); EXPECT(ghost(3)); EXPECT_EQ(remote_index(3), 3); } Field local = pointcloud.createField(option::name("local")); auto local_view = array::make_view(local); for (idx_t index = 0; index < pointcloud.size(); ++index) { local_view(index) = ghost(index) ? -1. : 10. * global_index(index); } local.haloExchange(); for (idx_t index = 0; index < pointcloud.size(); ++index) { const gidx_t grid_index = global_index(index) - 1; EXPECT_EQ(local_view(index), 10. * (grid.masterIndex(grid_index) + 1)); } Field global = pointcloud.createField(option::name("global") | option::global()); if (mpi::rank() == 0) { auto global_view = array::make_view(global); for (idx_t index = 0; index < global.size(); ++index) { global_view(index) = 100. + index; } } pointcloud.scatter(global, local); local.haloExchange(); for (idx_t index = 0; index < pointcloud.size(); ++index) { const gidx_t grid_index = global_index(index) - 1; EXPECT_EQ(local_view(index), 100. + grid.masterIndex(grid_index)); } for (idx_t index = 0; index < pointcloud.size(); ++index) { local_view(index) = 10. * global_index(index); } if (mpi::rank() == 0) { array::make_view(global).assign(-1.); } pointcloud.gather(local, global); if (mpi::rank() == 0) { auto global_view = array::make_view(global); EXPECT_EQ(global_view(0), -1.); for (idx_t index = 1; index < global.size() - 1; ++index) { EXPECT_EQ(global_view(index), 10. * (index + 1)); } EXPECT_EQ(global_view(global.size() - 1), -1.); } } //----------------------------------------------------------------------------- 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); } ecmwf-atlas-b0f79ee/src/tests/functionspace/fctest_functionspace.F900000664000175000017500000010005715263665111026100 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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() , "field" ) ! copied from template 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 ecmwf-atlas-b0f79ee/src/tests/functionspace/test_blockstructuredcolumns_checksum.cc0000664000175000017500000007640315263665111031466 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/MakeView.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/BlockStructuredColumns.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/Checksum.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas; using namespace atlas::functionspace; using namespace atlas::test; namespace { idx_t lane_value_count(const Field& field) { idx_t count = 1; for (idx_t dim = 1; dim < field.rank() - 1; ++dim) { count *= field.shape(dim); } return count; } template void fill_block_values(Value* data, const Field& field, idx_t dim, idx_t offset, idx_t block_size, Value& next_value) { if (dim == field.rank() - 1) { for (idx_t jlane = 0; jlane < block_size; ++jlane) { data[offset + jlane * field.stride(dim)] = next_value; next_value += Value{1}; } for (idx_t jlane = block_size; jlane < field.shape(dim); ++jlane) { data[offset + jlane * field.stride(dim)] = Value{-1}; } return; } for (idx_t index = 0; index < field.shape(dim); ++index) { fill_block_values(data, field, dim + 1, offset + index * field.stride(dim), block_size, next_value); } } template void pack_lane_values(std::vector& lane_values, const Field& field, const Value* data, idx_t dim, idx_t offset) { if (dim == field.rank() - 1) { lane_values.push_back(data[offset]); return; } for (idx_t index = 0; index < field.shape(dim); ++index) { pack_lane_values(lane_values, field, data, dim + 1, offset + index * field.stride(dim)); } } template std::string fill_field_and_expected_checksum(const BlockStructuredColumns& fs, Field& field) { auto* data = field.array().host_data(); std::vector lane_checksums; lane_checksums.reserve(fs.size()); std::vector lane_values; lane_values.reserve(lane_value_count(field)); Value next_value{1}; for (idx_t jblk = 0; jblk < fs.nblks(); ++jblk) { auto blk = fs.block(jblk); const idx_t block_offset = jblk * field.stride(0); fill_block_values(data, field, 1, block_offset, blk.size(), next_value); // increments next_value for (idx_t jrof = 0; jrof < blk.size(); ++jrof) { lane_values.clear(); if (field.rank() == 4) { // lev-outer, var-inner to match blocked checksum iteration order const idx_t nvar = field.shape(1); const idx_t nlev = field.shape(2); for (idx_t jlev = 0; jlev < nlev; ++jlev) { for (idx_t jvar = 0; jvar < nvar; ++jvar) { lane_values.push_back(data[block_offset + jvar * field.stride(1) + jlev * field.stride(2) + jrof * field.stride(3)]); } } } else { pack_lane_values(lane_values, field, data, 1, block_offset + jrof * field.stride(field.rank() - 1)); } lane_checksums.push_back(util::checksum(lane_values.data(), lane_values.size())); } } return util::checksum_to_hex_str(util::checksum(lane_checksums.data(), lane_checksums.size())); } template std::string expected_fieldset_checksum(const BlockStructuredColumns& fs, const FieldSet& fields) { std::vector lane_states(static_cast(fs.size())); for (auto& lane_state : lane_states) { util::checksum_reset(lane_state); } std::vector lane_values; for (idx_t jfld = 0; jfld < fields.size(); ++jfld) { const Field& field = fields[jfld]; const auto* data = field.array().host_data(); for (idx_t jblk = 0; jblk < fs.nblks(); ++jblk) { auto blk = fs.block(jblk); const idx_t block_offset = jblk * field.stride(0); for (idx_t jrof = 0; jrof < blk.size(); ++jrof) { lane_values.clear(); if (field.rank() == 4) { // lev-outer, var-inner to match blocked checksum iteration order const idx_t nvar = field.shape(1); const idx_t nlev = field.shape(2); for (idx_t jlev = 0; jlev < nlev; ++jlev) { for (idx_t jvar = 0; jvar < nvar; ++jvar) { lane_values.push_back(data[block_offset + jvar * field.stride(1) + jlev * field.stride(2) + jrof * field.stride(3)]); } } } else { pack_lane_values(lane_values, field, data, 1, block_offset + jrof * field.stride(field.rank() - 1)); } util::checksum_update(lane_states[static_cast(blk.index(jrof))], lane_values.data(), lane_values.size()); } } } std::vector lane_digests(lane_states.size()); for (size_t jlane = 0; jlane < lane_states.size(); ++jlane) { lane_digests[jlane] = util::checksum_digest(lane_states[jlane]); } return util::checksum_to_hex_str(util::checksum(lane_digests.data(), lane_digests.size())); } template Field make_non_contiguous_field(const BlockStructuredColumns& fs, const util::Config& options, std::vector& rawdata) { Field prototype = fs.createField(option::name("field") | options); idx_t block_data_size = 1; for (idx_t dim = 1; dim < prototype.rank(); ++dim) { block_data_size *= prototype.shape(dim); } const idx_t block_stride = block_data_size + 5; array::ArrayShape shape; array::ArrayStrides strides; shape.reserve(prototype.rank()); strides.resize(prototype.rank()); for (idx_t dim = 0; dim < prototype.rank(); ++dim) { shape.emplace_back(prototype.shape(dim)); } strides[prototype.rank() - 1] = 1; for (idx_t dim = prototype.rank() - 2; dim > 0; --dim) { strides[dim] = strides[dim + 1] * prototype.shape(dim + 1); } strides[0] = block_stride; rawdata.assign(fs.nblks() * block_stride, Value{-1}); return Field("field", rawdata.data(), array::ArraySpec{shape, strides}); } template Field make_block_slice_non_contiguous_field(const BlockStructuredColumns& fs, const util::Config& options, std::vector& rawdata) { constexpr idx_t inner_dim_padding = 1; constexpr idx_t block_padding = 3; Field prototype = fs.createField(option::name("field") | options); array::ArrayShape shape = prototype.shape(); array::ArrayStrides strides; strides.resize(prototype.rank()); strides[prototype.rank() - 1] = 1; ATLAS_ASSERT(prototype.rank() > 2, "We always expect nproma to be contiguous"); for (idx_t dim = prototype.rank() - 2; dim > 0; --dim) { // Add padding at each inner dimension so nproma-adjacent slices and higher dimensions are non-contiguous. strides[dim] = strides[dim + 1] * shape[dim + 1]; strides[dim] += inner_dim_padding; } // Extra spacing between consecutive blocks in dimension 0. strides[0] = strides[1] * shape[1] + block_padding; rawdata.assign(fs.nblks() * strides[0], Value{-1}); return Field("field", rawdata.data(), array::ArraySpec{shape, strides}); } template void expect_checksum_matches(const BlockStructuredColumns& fs, Field& field) { auto expected_checksum = fill_field_and_expected_checksum(fs, field); Log::info() << "Expected checksum: " << expected_checksum << std::endl; EXPECT_EQ(fs.checksum(field), expected_checksum); } template void update_lane_states_with_global_field(const Field& field, std::vector& lane_states) { ATLAS_ASSERT(field.shape(0) == static_cast(lane_states.size())); switch (field.rank()) { case 3: { auto value = array::make_view(field); for (idx_t point = 0; point < field.shape(0); ++point) { util::checksum_update(lane_states[static_cast(point)], &value(point, 0, 0), field.shape(1) * field.shape(2)); } break; } case 2: { auto value = array::make_view(field); for (idx_t point = 0; point < field.shape(0); ++point) { util::checksum_update(lane_states[static_cast(point)], &value(point, 0), field.shape(1)); } break; } case 1: { auto value = array::make_view(field); for (idx_t point = 0; point < field.shape(0); ++point) { util::checksum_update(lane_states[static_cast(point)], &value(point), 1); } break; } default: ATLAS_NOTIMPLEMENTED; } } util::checksum_t checksum_from_lane_states(const std::vector& lane_states) { std::vector lane_digests(lane_states.size()); for (size_t point = 0; point < lane_states.size(); ++point) { lane_digests[point] = util::checksum_digest(lane_states[point]); } return util::checksum(lane_digests.data(), lane_digests.size()); } template util::checksum_t expected_global_field_checksum(const Field& global_field) { std::vector lane_states(static_cast(global_field.shape(0))); for (auto& lane_state : lane_states) { util::checksum_reset(lane_state); } update_lane_states_with_global_field(global_field, lane_states); return checksum_from_lane_states(lane_states); } template util::checksum_t expected_global_fieldset_checksum(const FieldSet& global_fields) { ATLAS_ASSERT(global_fields.size() > 0); const idx_t npts = global_fields[0].shape(0); std::vector lane_states(static_cast(npts)); for (auto& lane_state : lane_states) { util::checksum_reset(lane_state); } for (idx_t jfld = 0; jfld < global_fields.size(); ++jfld) { const Field& global = global_fields[jfld]; update_lane_states_with_global_field(global, lane_states); } return checksum_from_lane_states(lane_states); } template util::checksum_t fill_global_field_and_expected_checksum(Field& global) { auto* data = global.array().host_data(); Value next_value{1}; for (idx_t i = 0; i < global.size(); ++i) { data[i] = next_value; next_value += 1.; } return expected_global_field_checksum(global); } } // namespace namespace atlas { namespace test { CASE("test_BlockStructuredColumns checksum includes partial last block (mpi-serial)") { using Value = double; mpi::Scope scope("self"); // MPI-serial execution in this scope constexpr idx_t nlev = 4; constexpr idx_t nvar = 3; auto fs = [] { auto grid = StructuredGrid("O8"); util::Config config; config.set("halo", 0); config.set("nproma", grid.size() - 1); return BlockStructuredColumns(grid, config); }(); REQUIRE(fs.size() > 1); REQUIRE(fs.size() % fs.nproma() == 1); REQUIRE(fs.block(fs.nblks() - 1).size() == 1); auto run_subcase = [&](const char* label, bool use_non_contiguous) { ATLAS_TRACE(label); Log::info() << "Running subcase: " << label << std::endl; if (use_non_contiguous) { std::vector rawdata; Field field = make_non_contiguous_field(fs, option::variables(nvar) | option::levels(nlev), rawdata); EXPECT(not field.contiguous()); expect_checksum_matches(fs, field); } else { Field field = fs.createField(option::name("field") | option::variables(nvar) | option::levels(nlev)); EXPECT(field.contiguous()); expect_checksum_matches(fs, field); } }; run_subcase("contiguous field", false); run_subcase("non-contiguous field", true); } CASE("test_BlockStructuredColumns checksum for various rank fields (mpi-serial)") { using Value = double; mpi::Scope scope("self"); auto fs = [] { StructuredGrid grid{"O8"}; util::Config config; config.set("halo", 0); config.set("nproma", grid.size() - 1); return BlockStructuredColumns(grid, config); }(); constexpr idx_t nvar = 3; constexpr idx_t nlev = 4; auto run_subcase = [&](const char* label, const util::Config& field_options, idx_t expected_rank) { ATLAS_TRACE(label); Log::info() << "Running variant: " << label << std::endl; Field contiguous = fs.createField(option::name("field") | field_options); EXPECT_EQ(contiguous.rank(), expected_rank); EXPECT(contiguous.contiguous()); expect_checksum_matches(fs, contiguous); std::vector rawdata; Field non_contiguous = make_non_contiguous_field(fs, field_options, rawdata); EXPECT_EQ(non_contiguous.rank(), expected_rank); EXPECT(not non_contiguous.contiguous()); expect_checksum_matches(fs, non_contiguous); }; run_subcase("four-dimensional field with levels and variables", option::variables(nvar) | option::levels(nlev), 4); run_subcase("three-dimensional field without levels", option::variables(nvar), 3); run_subcase("three-dimensional field without variables", option::levels(nlev), 3); run_subcase("two-dimensional field without levels and variables", util::Config(), 2); } CASE("test_BlockStructuredColumns checksum in mpi-serial uses generic path for block-slice non-contiguous fields") { using Value = double; mpi::Scope scope("self"); constexpr idx_t root = 0; constexpr idx_t nlev = 4; constexpr idx_t nvar = 3; auto fs = [] { auto grid = StructuredGrid("O8"); util::Config config; config.set("halo", 0); config.set("nproma", 5); return BlockStructuredColumns(grid, config); }(); Field global = fs.createField(option::name("global") | option::global(root) | option::variables(nvar) | option::levels(nlev)); util::checksum_t expected_checksum = 0; if (mpi::comm().rank() == root) { expected_checksum = fill_global_field_and_expected_checksum(global); } mpi::comm().broadcast(expected_checksum, root); std::vector rawdata; Field local = make_block_slice_non_contiguous_field(fs, option::variables(nvar) | option::levels(nlev), rawdata); EXPECT(not local.contiguous()); EXPECT(local.stride(local.rank() - 2) != local.shape(local.rank() - 1)); fs.scatter(global, local); auto expected_checksum_hex_str = util::checksum_to_hex_str(expected_checksum); Log::info() << "Expected checksum: " << expected_checksum_hex_str << std::endl; EXPECT_EQ(fs.checksum(local), expected_checksum_hex_str); } CASE("test_BlockStructuredColumns checksum(fieldset) matches checksum(field) for a single field (mpi-serial)") { using Value = double; mpi::Scope scope("self"); constexpr idx_t nlev = 4; constexpr idx_t nvar = 3; auto fs = [] { auto grid = StructuredGrid("O8"); util::Config config; config.set("halo", 0); config.set("nproma", grid.size() - 1); return BlockStructuredColumns(grid, config); }(); { Field field = fs.createField(option::name("field") | option::variables(nvar) | option::levels(nlev)); fill_field_and_expected_checksum(fs, field); FieldSet fieldset; fieldset.add(field); EXPECT_EQ(fs.checksum(fieldset), fs.checksum(field)); } { std::vector rawdata; Field field = make_non_contiguous_field(fs, option::variables(nvar) | option::levels(nlev), rawdata); fill_field_and_expected_checksum(fs, field); FieldSet fieldset; fieldset.add(field); EXPECT_EQ(fs.checksum(fieldset), fs.checksum(field)); } } CASE("test_BlockStructuredColumns checksum(fieldset) combines all fields as lane checksum updates (mpi-serial)") { using Value = double; mpi::Scope scope("self"); constexpr idx_t nlev = 4; constexpr idx_t nvar = 3; auto fs = [] { auto grid = StructuredGrid("O8"); util::Config config; config.set("halo", 0); config.set("nproma", 5); return BlockStructuredColumns(grid, config); }(); Field contiguous = fs.createField(option::name("contiguous") | option::variables(nvar) | option::levels(nlev)); std::vector rawdata; Field non_contiguous = make_non_contiguous_field(fs, option::variables(nvar) | option::levels(nlev), rawdata); fill_field_and_expected_checksum(fs, contiguous); fill_field_and_expected_checksum(fs, non_contiguous); FieldSet fieldset; fieldset.add(contiguous); fieldset.add(non_contiguous); const std::string expected = expected_fieldset_checksum(fs, fieldset); Log::info() << "Expected fieldset checksum: " << expected << std::endl; EXPECT_EQ(fs.checksum(fieldset), expected); } template void run_mpi_checksum_caseT(const functionspace::BlockStructuredColumns& fs, idx_t nlev, idx_t nvar) { constexpr idx_t root = 0; util::Config option_nlev_nvars; if (nvar > 0) { option_nlev_nvars.set(option::variables(nvar)); } if (nlev > 0) { option_nlev_nvars.set(option::levels(nlev)); } Field global = fs.createField(option::name("global") | option::global(root) | option_nlev_nvars); util::checksum_t expected_checksum = 0; if (mpi::comm().rank() == root) { expected_checksum = fill_global_field_and_expected_checksum(global); } mpi::comm().broadcast(expected_checksum, root); Field local = fs.createField(option::name("local") | option_nlev_nvars); fs.scatter(global, local); idx_t expected_rank = idx_t{2} /*nblks, nproma*/ + std::min(nlev,1) + std::min(nvar,1); EXPECT_EQ(local.rank(), expected_rank); auto expected_checksum_hex_str = util::checksum_to_hex_str(expected_checksum); Log::info() << "Expected checksum: " << expected_checksum_hex_str << std::endl; EXPECT_EQ(fs.checksum(local), expected_checksum_hex_str); }; void run_mpi_checksum_cases(const functionspace::BlockStructuredColumns& fs, idx_t nlev, idx_t nvar) { run_mpi_checksum_caseT(fs, nlev, nvar); run_mpi_checksum_caseT(fs, nlev, nvar); run_mpi_checksum_caseT(fs, nlev, nvar); run_mpi_checksum_caseT(fs, nlev, nvar); } template void run_mpi_fieldset_checksum_caseT(const functionspace::BlockStructuredColumns& fs, idx_t nlev, idx_t nvar) { constexpr idx_t root = 0; util::Config option_nlev_nvars; if (nvar > 0) { option_nlev_nvars.set(option::variables(nvar)); } if (nlev > 0) { option_nlev_nvars.set(option::levels(nlev)); } Field global_a = fs.createField(option::name("global_a") | option::global(root) | option_nlev_nvars); Field global_b = fs.createField(option::name("global_b") | option::global(root) | option_nlev_nvars); util::checksum_t expected_fieldset = 0; if (mpi::comm().rank() == root) { fill_global_field_and_expected_checksum(global_a); fill_global_field_and_expected_checksum(global_b); FieldSet global_fieldset; global_fieldset.add(global_a); global_fieldset.add(global_b); expected_fieldset = expected_global_fieldset_checksum(global_fieldset); } mpi::comm().broadcast(expected_fieldset, root); Field local_a = fs.createField(option::name("local_a") | option_nlev_nvars); Field local_b = fs.createField(option::name("local_b") | option_nlev_nvars); fs.scatter(global_a, local_a); fs.scatter(global_b, local_b); FieldSet local_fieldset; local_fieldset.add(local_a); local_fieldset.add(local_b); auto expected_fieldset_hex_str = util::checksum_to_hex_str(expected_fieldset); Log::info() << "Expected fieldset checksum (MPI): " << expected_fieldset_hex_str << std::endl; EXPECT_EQ(fs.checksum(local_fieldset), expected_fieldset_hex_str); } void run_mpi_fieldset_checksum_cases(const functionspace::BlockStructuredColumns& fs, idx_t nlev, idx_t nvar) { run_mpi_fieldset_checksum_caseT(fs, nlev, nvar); run_mpi_fieldset_checksum_caseT(fs, nlev, nvar); run_mpi_fieldset_checksum_caseT(fs, nlev, nvar); run_mpi_fieldset_checksum_caseT(fs, nlev, nvar); } CASE("test_BlockStructuredColumns checksum is deterministic in MPI") { auto fs = [] { auto grid = StructuredGrid("O8"); util::Config config; config.set("halo", 0); config.set("nproma", 8); return BlockStructuredColumns(grid, config); }(); constexpr idx_t nlev = 4; constexpr idx_t nvar = 3; auto run_subcase = [&](const char* label, idx_t levels, idx_t vars) { Log::info() << "Running subcase: " << label << std::endl; run_mpi_checksum_cases(fs, levels, vars); }; run_subcase("with variables and levels", nlev, nvar); run_subcase("with variables and no levels", 0, nvar); run_subcase("with levels and no variables", nlev, 0); run_subcase("with no variables and no levels", 0, 0); } CASE("test_BlockStructuredColumns checksum(fieldset) is deterministic in MPI") { auto fs = [] { auto grid = StructuredGrid("O8"); util::Config config; config.set("halo", 0); config.set("nproma", 8); return BlockStructuredColumns(grid, config); }(); constexpr idx_t nlev = 4; constexpr idx_t nvar = 3; auto run_subcase = [&](const char* label, idx_t levels, idx_t vars) { Log::info() << "Running fieldset subcase: " << label << std::endl; run_mpi_fieldset_checksum_cases(fs, levels, vars); }; run_subcase("with variables and levels", nlev, nvar); run_subcase("with variables and no levels", 0, nvar); run_subcase("with levels and no variables", nlev, 0); run_subcase("with no variables and no levels", 0, 0); } CASE("test_BlockStructuredColumns checksum with halo=2 (mpi-serial)") { using Value = double; mpi::Scope scope("self"); constexpr idx_t nlev = 4; constexpr idx_t nvar = 3; auto grid = StructuredGrid("O8"); auto fs_h0 = [&] { util::Config config; config.set("halo", 0); config.set("nproma", 8); return BlockStructuredColumns(grid, config); }(); auto fs_h2 = [&] { util::Config config; config.set("halo", 2); config.set("nproma", 8); return BlockStructuredColumns(grid, config); }(); Field field_h0 = fs_h0.createField(option::name("field_h0") | option::variables(nvar) | option::levels(nlev)); fill_field_and_expected_checksum(fs_h0, field_h0); Field global = fs_h0.createField(option::name("global") | option::global(0) | option::variables(nvar) | option::levels(nlev)); fs_h0.gather(field_h0, global); Field field_h2 = fs_h2.createField(option::name("field_h2") | option::variables(nvar) | option::levels(nlev)); fs_h2.scatter(global, field_h2); EXPECT_EQ(fs_h0.checksum(field_h0), fs_h2.checksum(field_h2)); std::vector rawdata; Field non_contiguous = make_non_contiguous_field(fs_h2, option::variables(nvar) | option::levels(nlev), rawdata); EXPECT(not non_contiguous.contiguous()); fill_field_and_expected_checksum(fs_h2, non_contiguous); EXPECT_EQ(fs_h2.checksum(non_contiguous), fs_h2.checksum(non_contiguous)); } CASE("test_BlockStructuredColumns checksum(fieldset) with halo=2 (mpi-serial)") { using Value = double; mpi::Scope scope("self"); constexpr idx_t nlev = 4; constexpr idx_t nvar = 3; auto grid = StructuredGrid("O8"); auto fs_h0 = [&] { util::Config config; config.set("halo", 0); config.set("nproma", 8); return BlockStructuredColumns(grid, config); }(); auto fs_h2 = [&] { util::Config config; config.set("halo", 2); config.set("nproma", 8); return BlockStructuredColumns(grid, config); }(); Field field_h0_a = fs_h0.createField(option::name("field_h0_a") | option::variables(nvar) | option::levels(nlev)); Field field_h0_b = fs_h0.createField(option::name("field_h0_b") | option::variables(2)); fill_field_and_expected_checksum(fs_h0, field_h0_a); fill_field_and_expected_checksum(fs_h0, field_h0_b); Field global_a = fs_h0.createField(option::name("global_a") | option::global(0) | option::variables(nvar) | option::levels(nlev)); Field global_b = fs_h0.createField(option::name("global_b") | option::global(0) | option::variables(2)); fs_h0.gather(field_h0_a, global_a); fs_h0.gather(field_h0_b, global_b); Field field_h2_a = fs_h2.createField(option::name("field_h2_a") | option::variables(nvar) | option::levels(nlev)); Field field_h2_b = fs_h2.createField(option::name("field_h2_b") | option::variables(2)); fs_h2.scatter(global_a, field_h2_a); fs_h2.scatter(global_b, field_h2_b); FieldSet fieldset_h0; FieldSet fieldset_h2; fieldset_h0.add(field_h0_a); fieldset_h0.add(field_h0_b); fieldset_h2.add(field_h2_a); fieldset_h2.add(field_h2_b); EXPECT_EQ(fs_h0.checksum(fieldset_h0), fs_h2.checksum(fieldset_h2)); } CASE("test_BlockStructuredColumns checksum with halo=2 matches halo=0 exactly (mpi-serial)") { using Value = double; mpi::Scope scope("self"); auto grid = StructuredGrid("O8"); auto fs_h0 = [&] { util::Config config; config.set("halo", 0); config.set("nproma", 8); return BlockStructuredColumns(grid, config); }(); auto fs_h2 = [&] { util::Config config; config.set("halo", 2); config.set("nproma", 8); return BlockStructuredColumns(grid, config); }(); Field field_h0 = fs_h0.createField(option::name("field_h0") | option::variables(3) | option::levels(4)); fill_field_and_expected_checksum(fs_h0, field_h0); Field global = fs_h0.createField(option::name("global") | option::global(0) | option::variables(3) | option::levels(4)); fs_h0.gather(field_h0, global); Field field_h2 = fs_h2.createField(option::name("field_h2") | option::variables(3) | option::levels(4)); fs_h2.scatter(global, field_h2); EXPECT_EQ(fs_h0.checksum(field_h0), fs_h2.checksum(field_h2)); Field field_h0_b = fs_h0.createField(option::name("field_h0_b") | option::variables(2)); fill_field_and_expected_checksum(fs_h0, field_h0_b); Field global_b = fs_h0.createField(option::name("global_b") | option::global(0) | option::variables(2)); fs_h0.gather(field_h0_b, global_b); Field field_h2_b = fs_h2.createField(option::name("field_h2_b") | option::variables(2)); fs_h2.scatter(global_b, field_h2_b); FieldSet fieldset_h0; FieldSet fieldset_h2; fieldset_h0.add(field_h0); fieldset_h0.add(field_h0_b); fieldset_h2.add(field_h2); fieldset_h2.add(field_h2_b); EXPECT_EQ(fs_h0.checksum(fieldset_h0), fs_h2.checksum(fieldset_h2)); } CASE("test_BlockStructuredColumns checksum with halo=2 matches halo=0 exactly (MPI)") { using Value = double; constexpr idx_t root = 0; auto grid = StructuredGrid("O8"); auto fs_h0 = [&] { util::Config config; config.set("halo", 0); config.set("nproma", 8); return BlockStructuredColumns(grid, config); }(); auto fs_h2 = [&] { util::Config config; config.set("halo", 2); config.set("nproma", 8); return BlockStructuredColumns(grid, config); }(); auto check_field_checksum_stable_with_halo = [&](const util::Config& options, Value first_value) { Field global = fs_h0.createField(option::name("global") | option::global(root) | options); if (mpi::comm().rank() == root) { fill_global_field_and_expected_checksum(global); // restart with requested first value for a deterministic variant per case auto* data = global.array().host_data(); Value next = first_value; for (idx_t i = 0; i < global.size(); ++i) { data[i] = next; next += Value{1}; } } Field local_h0 = fs_h0.createField(option::name("local_h0") | options); Field local_h2 = fs_h2.createField(option::name("local_h2") | options); fs_h0.scatter(global, local_h0); fs_h2.scatter(global, local_h2); EXPECT_EQ(fs_h0.checksum(local_h0), fs_h2.checksum(local_h2)); }; check_field_checksum_stable_with_halo(option::variables(3) | option::levels(4), Value{24000}); check_field_checksum_stable_with_halo(option::variables(3), Value{25000}); check_field_checksum_stable_with_halo(option::levels(4), Value{26000}); check_field_checksum_stable_with_halo(util::Config{}, Value{27000}); auto check_fieldset_checksum_stable_with_halo = [&](const util::Config& options_a, const util::Config& options_b) { Field global_a = fs_h0.createField(option::name("global_a") | option::global(root) | option::variables(2) | option::levels(3)); Field global_b = fs_h0.createField(option::name("global_b") | option::global(root) | option::variables(4)); if (mpi::comm().rank() == root) { fill_global_field_and_expected_checksum(global_a); fill_global_field_and_expected_checksum(global_b); } Field local_h0_a = fs_h0.createField(option::name("local_h0_a") | option::variables(2) | option::levels(3)); Field local_h0_b = fs_h0.createField(option::name("local_h0_b") | option::variables(4)); Field local_h2_a = fs_h2.createField(option::name("local_h2_a") | option::variables(2) | option::levels(3)); Field local_h2_b = fs_h2.createField(option::name("local_h2_b") | option::variables(4)); fs_h0.scatter(global_a, local_h0_a); fs_h0.scatter(global_b, local_h0_b); fs_h2.scatter(global_a, local_h2_a); fs_h2.scatter(global_b, local_h2_b); FieldSet fieldset_h0; FieldSet fieldset_h2; fieldset_h0.add(local_h0_a); fieldset_h0.add(local_h0_b); fieldset_h2.add(local_h2_a); fieldset_h2.add(local_h2_b); EXPECT_EQ(fs_h0.checksum(fieldset_h0), fs_h2.checksum(fieldset_h2)); }; check_fieldset_checksum_stable_with_halo(/*a*/ option::variables(2) | option::levels(3), /*b*/ option::variables(4)); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); }ecmwf-atlas-b0f79ee/src/tests/functionspace/test_spectral_functionspace.cc0000664000175000017500000002217015263665111027512 0ustar alastairalastair/* * (C) Copyright 2026 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/field/Field.h" #include "atlas/functionspace/Spectral.h" #include "atlas/option.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/parallel/omp/omp.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { namespace { constexpr int truncation = 79; constexpr int levels = 3; enum class ComplexComponent { Real, Imag }; double spectral_value(int n, int m, int level, ComplexComponent component) { return component == ComplexComponent::Imag ? -1000. - 100. * level - 10. * m - n : 1000. + 100. * level + 10. * m + n; } Field create_spectral_field(const functionspace::Spectral& spectral, const std::string& name) { Field field = spectral.createField(option::name(name) | option::levels(levels)); array::make_view(field).assign(-1.); return field; } Field create_global_spectral_field(const functionspace::Spectral& spectral, const std::string& name) { Field field = spectral.createField(option::name(name) | option::levels(levels) | option::global()); array::make_view(field).assign(-1.); return field; } void set_coefficient(array::ArrayView& data, idx_t real, idx_t imag, int n, int m) { for (idx_t level = 0; level < levels; ++level) { data(real, level) = spectral_value(n, m, static_cast(level), ComplexComponent::Real); data(imag, level) = spectral_value(n, m, static_cast(level), ComplexComponent::Imag); } } void fill_sequential_storage_order(const functionspace::Spectral& spectral, Field& field) { auto data = array::make_view(field); const auto zonal_wavenumbers = spectral.zonal_wavenumbers(); idx_t index = 0; for (idx_t jm = 0; jm < zonal_wavenumbers.size(); ++jm) { const int m = zonal_wavenumbers(jm); for (int n = m; n <= spectral.truncation(); ++n) { set_coefficient(data, index, index + 1, n, m); index += 2; } } EXPECT_EQ(index, field.shape(0)); } void fill_global_sequential_storage_order(const functionspace::Spectral& spectral, Field& field) { auto data = array::make_view(field); idx_t index = 0; if (mpi::comm().rank() == 0) { for (int m = 0; m <= spectral.truncation(); ++m) { for (int n = m; n <= spectral.truncation(); ++n) { set_coefficient(data, index, index + 1, n, m); index += 2; } } EXPECT_EQ(index, spectral.nb_spectral_coefficients_global()); } EXPECT_EQ(index, field.shape(0)); } void fill_nasm0_storage_order(const functionspace::Spectral& spectral, Field& field) { auto data = array::make_view(field); const auto zonal_wavenumbers = spectral.zonal_wavenumbers(); const auto nasm0 = spectral.offsets_by_zonal_wavenumber(); for (idx_t jm = 0; jm < zonal_wavenumbers.size(); ++jm) { const int m = zonal_wavenumbers(jm); idx_t index = nasm0[m]; for (int n = m; n <= spectral.truncation(); ++n, index+=2) { set_coefficient(data, index, index + 1, n, m); } } } void fill_inverted_nasm0_storage_order(const functionspace::Spectral& spectral, Field& field) { auto data = array::make_view(field); const auto zonal_wavenumbers = spectral.zonal_wavenumbers(); const auto nasm0 = spectral.offsets_by_zonal_wavenumber(); for (int n = 0; n <= spectral.truncation(); ++n) { for (idx_t jm = 0; jm < zonal_wavenumbers.size(); ++jm) { const int m = zonal_wavenumbers(jm); if (m > n) { continue; } const idx_t offset_for_zonal_wavenumber = nasm0[m]; const idx_t index = offset_for_zonal_wavenumber + 2 * (n - m); set_coefficient(data, index, index + 1, n, m); } } } void fill_parallel_for_order(const functionspace::Spectral& spectral, Field& field) { auto data = array::make_view(field); spectral.parallel_for([&](idx_t real, idx_t imag, int n, int m) { set_coefficient(data, real, imag, n, m); }); } void fill_global_parallel_for_order(const functionspace::Spectral& spectral, Field& field) { auto data = array::make_view(field); spectral.parallel_for(option::global(), [&](idx_t real, idx_t imag, int n, int m) { set_coefficient(data, real, imag, n, m); }); } void expect_field_matches_function(const functionspace::Spectral& spectral, const Field& field) { const auto data = array::make_view(field); spectral.parallel_for([&](idx_t real, idx_t imag, int n, int m) { for (idx_t level = 0; level < levels; ++level) { EXPECT_EQ(data(real, level), spectral_value(n, m, static_cast(level), ComplexComponent::Real)); EXPECT_EQ(data(imag, level), spectral_value(n, m, static_cast(level), ComplexComponent::Imag)); } }); } void expect_equivalent_fields(const Field& left, const Field& right) { EXPECT_EQ(left.shape(0), right.shape(0)); EXPECT_EQ(left.shape(1), right.shape(1)); const auto left_data = array::make_view(left); const auto right_data = array::make_view(right); for (idx_t coefficient = 0; coefficient < left.shape(0); ++coefficient) { for (idx_t level = 0; level < left.shape(1); ++level) { EXPECT_EQ(left_data(coefficient, level), right_data(coefficient, level)); } } } } // namespace //----------------------------------------------------------------------------- CASE("test_parallel_for_count") { const idx_t expected_global_coefficients = (truncation + 1) * (truncation + 2); functionspace::Spectral spectral(truncation); EXPECT_EQ(spectral.nb_spectral_coefficients_global(), expected_global_coefficients); // Following must be updated atomically in parallel_for idx_t parallel_for_count = 0; idx_t m0_count = 0; bool imag_follows_real = true; spectral.parallel_for([&](idx_t real, idx_t imag, int n, int m) { if (imag != real + 1) { atlas_omp_atomic_write imag_follows_real = false; } atlas_omp_atomic_update ++parallel_for_count; if (m == 0) { atlas_omp_atomic_update ++m0_count; } }); EXPECT(imag_follows_real); mpi::comm().allReduceInPlace(parallel_for_count, eckit::mpi::sum()); mpi::comm().allReduceInPlace(m0_count, eckit::mpi::sum()); EXPECT_EQ(2 * parallel_for_count, expected_global_coefficients); EXPECT_EQ(m0_count, truncation + 1); } CASE("test_spectral_sequential_loop_order") { functionspace::Spectral spectral(truncation); Field field = create_spectral_field(spectral, "sequential"); fill_sequential_storage_order(spectral, field); expect_field_matches_function(spectral, field); } CASE("test_spectral_nasm0_loop_order") { functionspace::Spectral spectral(truncation); Field sequential = create_spectral_field(spectral, "sequential"); Field with_nasm0 = create_spectral_field(spectral, "with_nasm0"); fill_sequential_storage_order(spectral, sequential); fill_nasm0_storage_order(spectral, with_nasm0); expect_equivalent_fields(sequential, with_nasm0); } CASE("test_spectral_inverted_loop_order") { functionspace::Spectral spectral(truncation); Field sequential = create_spectral_field(spectral, "sequential"); Field inverted = create_spectral_field(spectral, "inverted"); fill_sequential_storage_order(spectral, sequential); fill_inverted_nasm0_storage_order(spectral, inverted); expect_equivalent_fields(sequential, inverted); } CASE("test_spectral_parallel_for_loop_order") { functionspace::Spectral spectral(truncation); Field sequential = create_spectral_field(spectral, "sequential"); Field parallel_for = create_spectral_field(spectral, "parallel_for"); fill_sequential_storage_order(spectral, sequential); fill_parallel_for_order(spectral, parallel_for); expect_equivalent_fields(sequential, parallel_for); } CASE("test_spectral_global_parallel_for_loop_order") { functionspace::Spectral spectral(truncation); Field sequential = create_global_spectral_field(spectral, "global_sequential"); Field parallel_for = create_global_spectral_field(spectral, "global_parallel_for"); fill_global_sequential_storage_order(spectral, sequential); fill_global_parallel_for_order(spectral, parallel_for); expect_equivalent_fields(sequential, parallel_for); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); }ecmwf-atlas-b0f79ee/src/tests/functionspace/test_cellcolumns.cc0000664000175000017500000001203415263665111025272 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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("CellColumns owns its mask") { Mesh mesh = generate_mesh(); CellColumns first(mesh); CellColumns second(mesh); auto first_mask = array::make_view(first.mask()); auto second_mask = array::make_view(second.mask()); first_mask(0) = 0; EXPECT(second_mask(0) == 1); EXPECT(!mesh.cells().has_field("mask")); } 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); } ecmwf-atlas-b0f79ee/src/tests/functionspace/test_structuredcolumns_haloexchange.cc0000664000175000017500000000763615263665111031301 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/functionspace/test_structuredcolumns_regional.cc0000664000175000017500000001542015263665111030441 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 compute_xy reflects halo rows") { const int Nx = 8, Ny = 8; StructuredGrid::XSpace xspace(Config("type", "linear")("N", Nx)("start", 20.)("end", 60.)); StructuredGrid::YSpace yspace(Config("type", "linear")("N", Ny)("start", 20.)("end", 60.)); StructuredGrid grid(xspace, yspace); functionspace::StructuredColumns fs(grid, grid::Partitioner("checkerboard"), Config("halo", 2)); const idx_t i = 0; const idx_t north_jj = 2; const PointXY north_halo = fs.compute_xy(i, -2); EXPECT_EQ(north_halo.x(), grid.x(i, north_jj)); EXPECT_EQ(north_halo.y(), 2. * grid.y(0) - grid.y(north_jj)); const idx_t south_jj = Ny - 3; const PointXY south_halo = fs.compute_xy(i, Ny + 1); EXPECT_EQ(south_halo.x(), grid.x(i, south_jj)); EXPECT_EQ(south_halo.y(), 2. * grid.y(Ny - 1) - grid.y(south_jj)); } //----------------------------------------------------------------------------- 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/0000775000175000017500000000000015263665111021432 5ustar alastairalastairecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_structured2D_to_unstructured.cc0000664000175000017500000001420415263665111033673 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_spherical_vector_to_point_6PE.cc0000664000175000017500000001461415263665111033676 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_structured2D_regional.cc0000664000175000017500000002050515263665111032223 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_Triag2D.cc0000664000175000017500000001165115263665111024240 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_non_linear.cc0000664000175000017500000002274415263665111030104 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/CMakeLists.txt0000664000175000017500000001466615263665111024207 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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_interpolation_disable_halo_exchange SOURCES test_interpolation_disable_halo_exchange.cc LIBS atlas 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() ecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_finite_element_cached.cc0000664000175000017500000001610515263665111032230 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_structured2D_gpu.cc0000664000175000017500000002715715263665111031230 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_structured2D.cc0000664000175000017500000004356115263665111030352 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_cubedsphere.cc0000664000175000017500000002413415263665111030244 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_disable_halo_exchange.cc0000664000175000017500000001603715263665111032226 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/functionspace.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid.h" #include "atlas/grid/Partitioner.h" #include "atlas/interpolation.h" #include "atlas/interpolation/Interpolation.h" #include "atlas/util/Config.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { //----------------------------------------------------------------------------- class FunctionSpaces { public: FunctionSpaces(): source_functionspace_{Grid("O8"), option::halo(1)}, target_functionspace_{Grid("O24"), grid::MatchingPartitioner(source_functionspace_)} {} const functionspace::StructuredColumns& source() const { return source_functionspace_; } const functionspace::StructuredColumns& target() const { return target_functionspace_; } private: functionspace::StructuredColumns source_functionspace_{}; functionspace::StructuredColumns target_functionspace_{}; }; std::pair make_fields(const FunctionSpaces& fs) { const auto source_field = fs.source().createField(option::name("source")); const auto target_field = fs.target().createField(option::name("target")); return std::make_pair(source_field, target_field); } void assign_field(Field field, double owned_value, double ghost_value) { const auto ghost = array::make_view(field.functionspace().ghost()); auto view = array::make_view(field); for (int i = 0; i < view.size(); ++i) { view(i) = (ghost(i) ? ghost_value : owned_value); } } Interpolation create_interp_object(const FunctionSpaces& fs, bool allow_halo_exchange = true) { auto conf = option::type("structured-bilinear") | util::Config("ajoint", true); if (!allow_halo_exchange) { conf.set("allow_halo_exchange", false); } return Interpolation(conf, fs.source(), fs.target()); } CASE("Forward interpolation") { if constexpr (std::numeric_limits::has_quiet_NaN) { const auto function_spaces = FunctionSpaces(); const auto make_and_init_fields = [&] { auto [source_field, target_field] = make_fields(function_spaces); assign_field(source_field, 1., std::numeric_limits::quiet_NaN()); assign_field(target_field, 0., 0.); return std::make_pair(source_field, target_field); }; const auto has_nan = [](const Field& field) { const auto view = array::make_view(field); return std::any_of(view.data(), view.data() + view.size(), [](double value) { return std::isnan(value); }); }; SECTION("Automatic halo exchange enabled (default behaviour), manual=false, correct") { // Default behaviour. Halo exchange of source field triggers if field is dirty. // Interpolation has executed correctly. auto [source_field, target_field] = make_and_init_fields(); const auto interp = create_interp_object(function_spaces); interp.execute(source_field, target_field); EXPECT(!source_field.dirty()); EXPECT(!has_nan(target_field)); } SECTION("Automatic halo exchange disabled, manual=false, faulty") { // Automatic halo exchange of source field is disabled. // Interpolation is incorrect. Dirty halo data (NaNs) have propogated to target field. auto [source_field, target_field] = make_and_init_fields(); const auto interp = create_interp_object(function_spaces, false); interp.execute(source_field, target_field); EXPECT(source_field.dirty()); EXPECT(has_nan(target_field)); } SECTION("Manual halo exchange") { // Automatic halo exchange is disabled, but we manually perform it before interpolation. // Interpolation has executed correctly. auto [source_field, target_field] = make_and_init_fields(); source_field.haloExchange(); const auto interp = create_interp_object(function_spaces, false); interp.execute(source_field, target_field); EXPECT(!source_field.dirty()); EXPECT(!has_nan(target_field)); } } } CASE("Adjoint interpolation") { const auto function_spaces = FunctionSpaces(); const auto make_and_init_fields = [&] { auto [source_field, target_field] = make_fields(function_spaces); assign_field(source_field, 0., 0.); assign_field(target_field, 1., 1.); return std::make_pair(source_field, target_field); }; const auto has_empty_halos = [](Field& field) { const auto ghost = array::make_view(field.functionspace().ghost()); const auto view = array::make_view(field); for (int i = 0; i < view.size(); ++i) { if (ghost(i) && view(i) != 0.) { return false; } } return true; }; SECTION("Automatic halo exchange enabled (default behaviour)") { // Default behaviour. Adjoint of halo exchange always triggers after adjoint of interpolation. // Source field is valid and ready to use in further computations. auto [source_field, target_field] = make_and_init_fields(); const auto interp = create_interp_object(function_spaces); interp.execute_adjoint(source_field, target_field); EXPECT(!source_field.dirty()); EXPECT(has_empty_halos(source_field)); } SECTION("Automatic halo exchange disabled") { // Automatic adjoint halo exchange of source field is disabled. // Source field may contain sensitivity information in its halo. auto [source_field, target_field] = make_and_init_fields(); const auto interp = create_interp_object(function_spaces, false); interp.execute_adjoint(source_field, target_field); EXPECT(source_field.dirty()); EXPECT(!has_empty_halos(source_field)); } SECTION("Manual halo exchange") { // Automatic adjoint of halo exchange has been disabled, but we manually trigger it after adjoint of interpolation. // Source field is valid and ready to use in further computations. auto [source_field, target_field] = make_and_init_fields(); const auto interp = create_interp_object(function_spaces, false); interp.execute_adjoint(source_field, target_field); source_field.adjointHaloExchange(); EXPECT(!source_field.dirty()); EXPECT(has_empty_halos(source_field)); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_global_matrix.cc0000664000175000017500000002675315263665111030610 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_structured2D_to_points.cc0000664000175000017500000001207315263665111032442 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_finite_element.cc0000664000175000017500000001470315263665111030743 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_structured2D_limiter.cc0000664000175000017500000002706015263665111032073 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_Quad2D.cc0000664000175000017500000001462515263665111024070 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_Quad3D.cc0000664000175000017500000001402615263665111024064 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/CubicInterpolationPrototype.h0000664000175000017500000004053115263665111027331 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_k_nearest_neighbours.cc0000664000175000017500000000675715263665111032166 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_structured2D_to_points_gpu.cc0000664000175000017500000001166015263665111033316 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_conservative.cc0000664000175000017500000002512415263665111030463 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 = 0.; // check polygon intersections EXPECT(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]); EXPECT(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 EXPECT(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]); EXPECT(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 EXPECT(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]); EXPECT(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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_binning.cc0000664000175000017500000003105415263665111027376 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_spherical_vector.cc0000664000175000017500000006605315263665111031315 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.); const auto sourceGhostView = array::make_view(sourceFunctionSpace.ghost()); for (auto idx = idx_t{0}; idx < sourceFunctionSpace.size(); idx++) { if (sourceGhostView(idx)) { continue; } std::tie(uSourceView(idx), vSourceView(idx)) = vortexHorizontal(sourceLonLatView(idx, 0), sourceLonLatView(idx, 1)); } // Copy non-halo exchanged source fields. auto uOriginalSource = sourceFieldSet["u"].clone(); auto vOriginalSource = sourceFieldSet["v"].clone(); const auto uOriginalSourceView = array::make_view(uOriginalSource); const auto vOriginalSourceView = array::make_view(vOriginalSource); 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); // Check that adjoint of halo exchange still triggers on "clean" fields. interp.execute_adjoint(sourceAdjointFieldSet, targetAdjointFieldSet); constexpr auto tinyNum = 1e-13; const auto targetDotTarget = dotProduct(uTargetView, uTargetView) + dotProduct(vTargetView, vTargetView); const auto sourceDotSourceAdjoint = dotProduct(uOriginalSourceView, uSourceAdjointView) + dotProduct(vOriginalSourceView, 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_unstructured_bilinear_lonlat.cc0000664000175000017500000001325615263665111033743 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_from_lam.cc0000664000175000017500000003775715263665111027566 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_cubic_prototype.cc0000664000175000017500000003577215263665111031177 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/interpolation/test_interpolation_grid_box_average.cc0000664000175000017500000002221715263665111031242 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); } ecmwf-atlas-b0f79ee/src/tests/redistribution/0000775000175000017500000000000015263665111021611 5ustar alastairalastairecmwf-atlas-b0f79ee/src/tests/redistribution/fctest_redistribution.F900000664000175000017500000000743515263665111026520 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/redistribution/test_redistribution_structured.cc0000664000175000017500000003305615263665111030520 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); } ecmwf-atlas-b0f79ee/src/tests/redistribution/test_redistribution_generic.cc0000664000175000017500000007736115263665111027737 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); } ecmwf-atlas-b0f79ee/src/tests/redistribution/CMakeLists.txt0000664000175000017500000000300415263665111024346 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() ecmwf-atlas-b0f79ee/src/tests/CMakeLists.txt0000664000175000017500000000714515263665111021312 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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 ) ecmwf-atlas-b0f79ee/src/tests/mesh/0000775000175000017500000000000015263665111017477 5ustar alastairalastairecmwf-atlas-b0f79ee/src/tests/mesh/test_shapefunctions.cc0000664000175000017500000006320515263665111024104 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/mesh/fctest_connectivity.F900000664000175000017500000002052115263665111024045 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/mesh/test_meshgen_splitcomm.cc0000664000175000017500000001677615263665111024603 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/mesh/test_mesh_triangular_mesh_builder.cc0000664000175000017500000000460215263665111026755 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); } ecmwf-atlas-b0f79ee/src/tests/mesh/fctest_mesh.F900000664000175000017500000001517715263665111022276 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 call atlas_log%info( "--- test_mesh_nodes" ) 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 TEST( test_mesh_grid_config ) implicit none type(atlas_Grid) :: grid type(atlas_Config) :: config type(atlas_Mesh) :: mesh type(atlas_mesh_Nodes) :: nodes type(atlas_mesh_Cells) :: cells type(atlas_Elements) :: quads, triangles call atlas_log%info( "--- test_mesh_grid_config" ) grid = atlas_Grid("N16") config = atlas_Config() call config%set("triangulate", .true.) mesh = atlas_Mesh(grid, config) nodes = mesh%nodes() cells = mesh%cells() quads = cells%elements(1) triangles = cells%elements(2) FCTEST_CHECK_EQUAL( nodes%size(), 1720 ) FCTEST_CHECK_EQUAL( quads%size(), 0 ) FCTEST_CHECK_EQUAL( triangles%size(), 3372 ) call triangles%final() call quads%final() call cells%final() call nodes%final() call mesh%final() call config%final() call grid%final() END_TEST TEST( test_mesh_grid_distribution ) implicit none type(atlas_Grid) :: grid type(atlas_GridDistribution) :: distribution type(atlas_Partitioner) :: partitioner type(atlas_Mesh) :: mixed_mesh type(atlas_mesh_Nodes) :: mixed_nodes type(atlas_mesh_Cells) :: mixed_cells type(atlas_Elements) :: mixed_quads, mixed_triangles type(atlas_ElementType) :: element_type call atlas_log%info( "--- test_mesh_grid_distribution" ) grid = atlas_Grid("N16") partitioner = atlas_Partitioner("serial") distribution = atlas_GridDistribution(grid, partitioner) mixed_mesh = atlas_Mesh(grid, distribution) mixed_nodes = mixed_mesh%nodes() FCTEST_CHECK( mixed_nodes%size() > 0 ) mixed_cells = mixed_mesh%cells() FCTEST_CHECK_EQUAL( mixed_cells%nb_types(), 2 ) mixed_quads = mixed_cells%elements(1) mixed_triangles = mixed_cells%elements(2) FCTEST_CHECK_EQUAL( mixed_nodes%size(), 1720 ) FCTEST_CHECK_EQUAL( mixed_quads%size(), 1080 ) FCTEST_CHECK_EQUAL( mixed_triangles%size(), 1212 ) FCTEST_CHECK( mixed_quads%size() > 0 ) FCTEST_CHECK( mixed_triangles%size() > 0 ) element_type = mixed_quads%element_type() FCTEST_CHECK_EQUAL( element_type%name(), "Quadrilateral" ) call element_type%final() element_type = mixed_triangles%element_type() FCTEST_CHECK_EQUAL( element_type%name(), "Triangle" ) call element_type%final() call mixed_quads%final() call mixed_triangles%final() call mixed_cells%final() call mixed_nodes%final() call mixed_mesh%final() call distribution%final() call partitioner%final() call grid%final() END_TEST TEST( test_mesh_grid_partitioner_config ) implicit none type(atlas_Grid) :: grid type(atlas_Partitioner) :: partitioner type(atlas_Config) :: config type(atlas_Mesh) :: triangular_mesh type(atlas_mesh_Nodes) :: triangular_nodes type(atlas_mesh_Cells) :: triangular_cells type(atlas_Elements) :: triangular_quads, triangular_triangles type(atlas_ElementType) :: element_type call atlas_log%info( "--- test_mesh_grid_partitioner_config" ) grid = atlas_Grid("N16") partitioner = atlas_Partitioner("serial") config = atlas_Config() call config%set("triangulate", .true.) triangular_mesh = atlas_Mesh(grid, partitioner, config) triangular_nodes = triangular_mesh%nodes() FCTEST_CHECK( triangular_nodes%size() > 0 ) triangular_cells = triangular_mesh%cells() FCTEST_CHECK_EQUAL( triangular_cells%nb_types(), 2 ) triangular_quads = triangular_cells%elements(1) triangular_triangles = triangular_cells%elements(2) FCTEST_CHECK_EQUAL( triangular_nodes%size(), 1720 ) FCTEST_CHECK_EQUAL( triangular_triangles%size(), 3372 ) FCTEST_CHECK_EQUAL( triangular_quads%size(), 0 ) FCTEST_CHECK( triangular_triangles%size() > 0 ) element_type = triangular_triangles%element_type() FCTEST_CHECK_EQUAL( element_type%name(), "Triangle" ) call element_type%final() call triangular_quads%final() call triangular_triangles%final() call triangular_cells%final() call triangular_nodes%final() call triangular_mesh%final() call config%final() call partitioner%final() call grid%final() END_TEST TEST( test_matching_partitioner ) implicit none type(atlas_Grid) :: grid type(atlas_Partitioner) :: partitioner, matching_partitioner type(atlas_Mesh) :: mesh type(atlas_FunctionSpace) :: functionspace call atlas_log%info( "--- test_matching_partitioner" ) grid = atlas_Grid("N16") partitioner = atlas_Partitioner("serial") mesh = atlas_Mesh(grid, partitioner) matching_partitioner = atlas_MatchingPartitioner(mesh) call matching_partitioner%final() functionspace = atlas_functionspace_StructuredColumns(grid, partitioner) matching_partitioner = atlas_MatchingPartitioner(functionspace) call matching_partitioner%final() call functionspace%final() call mesh%final() call partitioner%final() call grid%final() END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE ecmwf-atlas-b0f79ee/src/tests/mesh/test_mesh_reorder_unstructured.geo0000664000175000017500000000037315263665111026542 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}; ecmwf-atlas-b0f79ee/src/tests/mesh/fctest_meshgen.F900000664000175000017500000001321715263665111022761 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/mesh/test_connectivity.cc0000664000175000017500000003073715263665111023575 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/mesh/CMakeLists.txt0000664000175000017500000001306115263665111022240 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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_executable( TARGET atlas_test_gauss_with_cs_partition SOURCES test_gauss_with_cs_partition.cc LIBS atlas NOINSTALL ) foreach( mpi_size 1 6 12 54 ) ecbuild_add_test( TARGET atlas_test_gauss_with_cs_partition_MPI_${mpi_size} COMMAND atlas_test_gauss_with_cs_partition MPI ${mpi_size} CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL ${mpi_size} ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endforeach() 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 ) ecmwf-atlas-b0f79ee/src/tests/mesh/test_connectivity_kernel.hic0000664000175000017500000001056115263665111025304 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 ); } ecmwf-atlas-b0f79ee/src/tests/mesh/helper_mesh_builder.h0000664000175000017500000000744215263665111023660 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 ecmwf-atlas-b0f79ee/src/tests/mesh/test_elements.cc0000664000175000017500000003563015263665111022670 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/mesh/test_mesh_builder_parallel.cc0000664000175000017500000003023415263665111025365 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); } ecmwf-atlas-b0f79ee/src/tests/mesh/test_stream_connectivity.cc0000664000175000017500000001116115263665111025136 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/mesh/test_distmesh.cc0000664000175000017500000001076115263665111022672 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/mesh/test_footprint.cc0000664000175000017500000000563015263665111023075 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 "atlas/array/Array.h" #include "atlas/array/ArrayView.h" #include "atlas/field/Field.h" #include "atlas/grid/Grid.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/meshgenerator.h" #include "atlas/runtime/Log.h" #include "atlas/util/Metadata.h" #include "tests/AtlasTestEnvironment.h" using namespace eckit; using namespace atlas::util; namespace atlas { namespace test { static std::string griduid() { return "O64"; } //----------------------------------------------------------------------------- CASE("test_footprint") { array::ArrayT 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); } ecmwf-atlas-b0f79ee/src/tests/mesh/test_mesh_builder.cc0000664000175000017500000003000615263665111023506 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); } ecmwf-atlas-b0f79ee/src/tests/mesh/test_meshgen3d.cc0000664000175000017500000000514315263665111022725 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/mesh/test_rgg.cc0000664000175000017500000004673215263665111021640 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/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_patch_quads_with_odd_rims") { auto check_five_node_pole_rims = [](const StructuredGrid& grid, const std::string& output_prefix) { ATLAS_ASSERT(grid.ny() > 0); ATLAS_ASSERT(grid.nx(0) == 5); ATLAS_ASSERT(grid.nx(grid.ny() - 1) == 5); util::Config triangle_options; triangle_options.set("3d", false); triangle_options.set("include_pole", false); triangle_options.set("patch_quads", false); util::Config mixed_options = triangle_options; mixed_options.set("patch_quads", true); auto triangular_mesh = StructuredMeshGenerator(triangle_options).generate(grid); auto patched_mesh = StructuredMeshGenerator(mixed_options).generate(grid); // Each five-node pole rim changes from three triangles to one quad and one triangle. EXPECT_EQ(patched_mesh.cells().elements(0).size(), triangular_mesh.cells().elements(0).size() + 2); EXPECT_EQ(patched_mesh.cells().elements(1).size(), triangular_mesh.cells().elements(1).size() - 4); auto patched_flags = array::make_view(patched_mesh.cells().flags()); using CellTopology = mesh::Nodes::Topology; idx_t nb_patch_cells = 0; for (idx_t jcell = 0; jcell < patched_mesh.cells().size(); ++jcell) { nb_patch_cells += CellTopology::check(patched_flags(jcell), CellTopology::PATCH) ? 1 : 0; } EXPECT_EQ(nb_patch_cells, 4); output::Gmsh(output_prefix + "_xyz.msh", util::Config("coordinates", "xyz")).write(patched_mesh); output::Gmsh(output_prefix + "_xy.msh", util::Config("coordinates", "xy")).write(patched_mesh); }; SECTION("periodic grid") { int nlat = 2; long lon[] = {5, 7}; // will be mirrored by equator check_five_node_pole_rims(test::minimal_grid(nlat, lon), "test_rgg_patch_quads_periodic"); } SECTION("non-periodic grid") { std::vector nx{5, 7, 7, 5}; StructuredGrid grid = ReducedGaussianGrid(nx, RectangularDomain({0., 320.}, {-90., 90.})); EXPECT(!grid.periodic()); EXPECT_EQ(grid.nx(0), 5); EXPECT_EQ(grid.nx(1), 7); EXPECT_EQ(grid.nx(2), 7); EXPECT_EQ(grid.nx(3), 5); EXPECT_EQ(grid.ny(), 4); check_five_node_pole_rims(grid, "test_rgg_patch_quads_non_periodic"); } } 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); } ecmwf-atlas-b0f79ee/src/tests/mesh/test_mesh_reorder_unstructured.msh0000664000175000017500000004754615263665111026574 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 ecmwf-atlas-b0f79ee/src/tests/mesh/test_halo.cc0000664000175000017500000003222615263665111021775 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 "< #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); } ecmwf-atlas-b0f79ee/src/tests/mesh/test_cgal_mesh_gen_from_points.cc0000664000175000017500000000242515263665111026242 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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; } ecmwf-atlas-b0f79ee/src/tests/mesh/test_gauss_with_cs_partition.cc0000664000175000017500000000340715263665111026004 0ustar alastairalastair#include "atlas/functionspace/NodeColumns.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator/MeshGenerator.h" #include "atlas/grid/Partitioner.h" #include "atlas/output/Gmsh.h" #include "atlas/util/Config.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::mesh; namespace atlas { namespace test { // ------------------------------------------------------------------ void test(std::string cs_gridname, std::string g_gridname) { const auto cs_grid = Grid(cs_gridname); const auto cs_meshgen = MeshGenerator{"cubedsphere_dual", util::Config("partitioner", "cubedsphere") | util::Config("halo", 1)}; const auto cs_mesh = cs_meshgen.generate(cs_grid); const auto cs_fs = functionspace::NodeColumns(cs_mesh); const auto cs_partitioner = grid::MatchingMeshPartitioner(cs_fs.mesh(), option::type("cubedsphere")); const auto g_grid = StructuredGrid(g_gridname); const auto g_mesh = atlas::StructuredMeshGenerator().generate(g_grid, cs_partitioner); // For human verification output::Gmsh gmsh{g_gridname + "_with_" + cs_gridname + "_np" + std::to_string(cs_partitioner.nb_partitions()) + ".msh"}; gmsh.write(g_mesh); } CASE("CS12 O12") { test("CS-LFR-12", "O12"); } CASE("CS12 F12") { test("CS-LFR-12", "F12"); } CASE("CS15 O15") { test("CS-LFR-15", "O15"); } CASE("CS15 F15") { test("CS-LFR-15", "F15"); } CASE("CS12 O8") { test("CS-LFR-12", "O8"); } CASE("CS12 F8") { test("CS-LFR-12", "F8"); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); }ecmwf-atlas-b0f79ee/src/tests/mesh/test_pentagon_element.cc0000664000175000017500000001014115263665111024366 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/mesh/test_cubedsphere_meshgen.cc0000664000175000017500000005004115263665111025044 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); } ecmwf-atlas-b0f79ee/src/tests/mesh/test_mesh_reorder.cc0000664000175000017500000007304715263665111023536 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/mesh/test_healpixmeshgen.cc0000664000175000017500000002031515263665111024047 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/mesh/fctest_elements.F900000664000175000017500000002221715263665111023147 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/mesh/test_mesh_node2cell.cc0000664000175000017500000005421015263665111023732 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/mesh/test_ll.cc0000664000175000017500000000204015263665111021450 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/mesh/fctest_mesh_triangular_mesh_builder.F900000664000175000017500000001042215263665111027234 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/mesh/test_mesh_build_edges.cc0000664000175000017500000007043415263665111024337 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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, Config("pole_edges", false)("node_order", "global_index")("cell_order", "left_first")); 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(); const mesh::HybridElements::Connectivity& edge_cell_connectivity = mesh.edges().cell_connectivity(); const auto node_gidx = array::make_view(mesh.nodes().global_index()); EXPECT(mesh.projection().units() == "degrees"); EXPECT_EQ(mesh.edges().size(), edge_nodes_check.size() / 2); for (idx_t jedge = 0; jedge < mesh.edges().size(); ++jedge) { const idx_t expected_node1 = edge_nodes_check[2 * jedge + 0]; const idx_t expected_node2 = edge_nodes_check[2 * jedge + 1]; const idx_t node1 = edge_node_connectivity(jedge, 0); const idx_t node2 = edge_node_connectivity(jedge, 1); EXPECT((node1 == expected_node1 && node2 == expected_node2) || (node1 == expected_node2 && node2 == expected_node1)); if (edge_cell_connectivity(jedge, 1) != edge_cell_connectivity.missing_value()) { EXPECT(node_gidx(node1) < node_gidx(node2)); } } } 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 mesh::HybridElements::Connectivity& edge_node_connectivity = mesh.edges().node_connectivity(); const auto node_xyz = array::make_view(mesh.nodes().field("xyz")); EXPECT_EQ(mesh.edges().size(), edge_to_cell_check.size() / 2); for (idx_t jedge = 0; jedge < mesh.edges().size(); ++jedge) { const idx_t expected_e1 = edge_to_cell_check[2 * jedge + 0]; const idx_t expected_e2 = edge_to_cell_check[2 * jedge + 1]; const idx_t e1 = edge_cell_connectivity(jedge, 0); const idx_t e2 = edge_cell_connectivity(jedge, 1); EXPECT((e1 == expected_e1 && e2 == expected_e2) || (e1 == expected_e2 && e2 == expected_e1)); const idx_t node1 = edge_node_connectivity(jedge, 0); const idx_t node2 = edge_node_connectivity(jedge, 1); const double normal_x = node_xyz(node1, YY) * node_xyz(node2, ZZ) - node_xyz(node1, ZZ) * node_xyz(node2, YY); const double normal_y = node_xyz(node1, ZZ) * node_xyz(node2, XX) - node_xyz(node1, XX) * node_xyz(node2, ZZ); const double normal_z = node_xyz(node1, XX) * node_xyz(node2, YY) - node_xyz(node1, YY) * node_xyz(node2, XX); double centre_x = 0.; double centre_y = 0.; double centre_z = 0.; for (idx_t jnode = 0; jnode < cell_node_connectivity.cols(e1); ++jnode) { const idx_t cell_node = cell_node_connectivity(e1, jnode); centre_x += node_xyz(cell_node, XX); centre_y += node_xyz(cell_node, YY); centre_z += node_xyz(cell_node, ZZ); } EXPECT(normal_x * centre_x + normal_y * centre_y + normal_z * centre_z >= 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_node_order_xy") { Grid grid("O2"); StructuredMeshGenerator generator(Config("angle", 29.0)("triangulate", false)("ghost_at_end", false)); Mesh mesh = generator.generate(grid); mesh::actions::build_edges(mesh, Config("pole_edges", false)("node_order", "xy")); const auto& edge_nodes = mesh.edges().node_connectivity(); const auto& edge_cells = mesh.edges().cell_connectivity(); const auto& cell_nodes = mesh.cells().node_connectivity(); UniqueLonLat compute_uid(mesh); for (idx_t edge = 0; edge < mesh.edges().size(); ++edge) { EXPECT(compute_uid(edge_nodes(edge, 0)) <= compute_uid(edge_nodes(edge, 1))); const idx_t cell1 = edge_cells(edge, 0); const idx_t cell2 = edge_cells(edge, 1); EXPECT(cell1 != edge_cells.missing_value()); if (cell2 != edge_cells.missing_value()) { EXPECT(compute_uid(cell_nodes.row(cell1)) <= compute_uid(cell_nodes.row(cell2))); } } } 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); } ecmwf-atlas-b0f79ee/src/tests/projection/0000775000175000017500000000000015263665111020717 5ustar alastairalastairecmwf-atlas-b0f79ee/src/tests/projection/test_jacobian.cc0000664000175000017500000001651415263665111024042 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/MD5.h" #include "atlas/grid.h" #include "atlas/projection.h" #include "atlas/util/Config.h" #include "atlas/util/Point.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { //----------------------------------------------------------------------------- Projection::Jacobian getJacobian(const Projection& proj, const PointLonLat& lonlat0) { double dlon = 0.0001, dlat = 0.0001; double xy0[2] = {lonlat0.lon() + 0.00, lonlat0.lat() + 0.00}; double xy1[2] = {lonlat0.lon() + dlon, lonlat0.lat() + 0.00}; double xy2[2] = {lonlat0.lon() + 0.00, lonlat0.lat() + dlat}; proj.lonlat2xy(xy0); proj.lonlat2xy(xy1); proj.lonlat2xy(xy2); Projection::Jacobian jac; jac[0] = {(xy1[0] - xy0[0]) / dlon, (xy2[0] - xy0[0]) / dlat}; jac[1] = {(xy1[1] - xy0[1]) / dlon, (xy2[1] - xy0[1]) / dlat}; return jac; } void printJacobian(const Projection::Jacobian& jac, const std::string& name) { printf(" %s = \n", name.c_str()); printf(" %12.4f, %12.4f | %12.4f\n", jac[0][0], jac[0][1], sqrt(jac[0][0] * jac[0][0] + jac[0][1] * jac[0][1])); printf(" %12.4f, %12.4f | %12.4f\n", jac[1][0], jac[1][1], sqrt(jac[1][0] * jac[1][0] + jac[1][1] * jac[1][1])); printf(" %12.4f, %12.4f\n", sqrt(jac[0][0] * jac[0][0] + jac[1][0] * jac[1][0]), sqrt(jac[0][1] * jac[0][1] + jac[1][1] * jac[1][1])); } template 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); } ecmwf-atlas-b0f79ee/src/tests/projection/CMakeLists.txt0000664000175000017500000000277315263665111023470 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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} ) ecmwf-atlas-b0f79ee/src/tests/projection/test_proj.cc0000664000175000017500000000316715263665111023246 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/projection/test_projection_variable_resolution.cc0000664000175000017500000006176115263665111030604 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) { ///::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); } ecmwf-atlas-b0f79ee/src/tests/projection/test_projection_LAEA.cc0000664000175000017500000001556015263665111025232 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/projection/test_rotation.cc0000664000175000017500000002543415263665111024134 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/projection/fctest_projection.F900000664000175000017500000001167215263665111024732 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/projection/test_bounding_box.cc0000664000175000017500000002223415263665111024745 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); } ecmwf-atlas-b0f79ee/src/tests/TestMeshes.h0000664000175000017500000000173115263665111021002 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/tests/io/0000775000175000017500000000000015263665111017152 5ustar alastairalastairecmwf-atlas-b0f79ee/src/tests/io/CMakeLists.txt0000664000175000017500000000205115263665111021710 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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() ecmwf-atlas-b0f79ee/src/tests/io/test_io_record.cc0000664000175000017500000005071615263665111022476 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/io/test_io_encoding.cc0000664000175000017500000007166315263665111023012 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/AtlasTestEnvironment.h0000664000175000017500000004716215263665111023057 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 float& v1, const float& v2, const double& t) { if (t != 0. && t < std::numeric_limits::denorm_min()) { throw std::underflow_error("Tolerance underflows float precision and will become 0.0"); } return is_approximately_equal(v1, v2, static_cast(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 ecmwf-atlas-b0f79ee/src/tests/field/0000775000175000017500000000000015263665111017626 5ustar alastairalastairecmwf-atlas-b0f79ee/src/tests/field/test_fieldset.cc0000664000175000017500000001024515263665111022775 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/field/fctest_field_wrap_gpu.F900000664000175000017500000001715615263665111024457 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/field/test_field_memory_resource.cc0000664000175000017500000000765215263665111025570 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); } ecmwf-atlas-b0f79ee/src/tests/field/fctest_field_gpu.F900000664000175000017500000000734015263665111023420 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/field/fctest_field_wrap.F900000664000175000017500000001076715263665111023605 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/field/CMakeLists.txt0000664000175000017500000001060715263665111022372 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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() ecmwf-atlas-b0f79ee/src/tests/field/external_acc_routine.F900000664000175000017500000000113615263665111024304 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/field/test_field_missingvalue.cc0000664000175000017500000002311715263665111025051 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/Field.h" #include "atlas/field/MissingValue.h" #include "atlas/util/Metadata.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("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); } ecmwf-atlas-b0f79ee/src/tests/field/test_field_foreach.cc0000664000175000017500000004420315263665111023751 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 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 ecmwf-atlas-b0f79ee/src/tests/field/test_field_halo.cc0000664000175000017500000000613415263665111023266 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); } ecmwf-atlas-b0f79ee/src/tests/field/fctest_field_host.F900000664000175000017500000000331515263665111023600 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/field/test_field_acc.cc0000664000175000017500000001541015263665111023066 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/field/fctest_fieldset_gpu.F900000664000175000017500000002360015263665111024131 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/field/test_multifield_ifs.cc0000664000175000017500000003702615263665111024203 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/runtime/0000775000175000017500000000000015263665111020226 5ustar alastairalastairecmwf-atlas-b0f79ee/src/tests/runtime/test_library.cc0000664000175000017500000000105315263665111023237 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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(); } ecmwf-atlas-b0f79ee/src/tests/runtime/CMakeLists.txt0000664000175000017500000000266215263665111022774 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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 ) ecbuild_add_test( TARGET atlas_test_debug_logging SOURCES test_debug_logging.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ATLAS_MPI_BARRIER_TIMEOUT=3 ) ecbuild_add_test( TARGET atlas_test_debug_logging_mpi COMMAND atlas_test_debug_logging MPI 3 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 3 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ATLAS_MPI_BARRIER_TIMEOUT=3 ) 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() ecmwf-atlas-b0f79ee/src/tests/runtime/test_library_noargs.cc0000664000175000017500000000152115263665111024610 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 } ecmwf-atlas-b0f79ee/src/tests/runtime/test_debug_logging.cc0000664000175000017500000002260515263665111024375 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/parallel/mpi/mpi.h" #include "atlas/runtime/Log.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { CASE("test_debug_synchronized_logging_basic") { // Test that ATLAS_DEBUG properly logs from multiple MPI ranks // without race conditions or garbled output. const auto& comm = mpi::comm(); const int rank = comm.rank(); const int size = comm.size(); Log::info() << "[Rank " << rank << "] Starting basic debug test" << std::endl; // All ranks call ATLAS_DEBUG at roughly the same time // This creates contention in the logging system which could expose // race conditions if buffers are not properly synchronized ATLAS_DEBUG("All ranks logging simultaneously from basic test"); // Verify each rank can log independently after the debug call Log::info() << "[Rank " << rank << "/" << size << "] Post-debug logging" << std::endl; ATLAS_DEBUG_VAR(backtrace()); mpi::comm().barrier(); Log::info() << "[Rank " << rank << "] Basic debug test completed" << std::endl; } CASE("test_debug_with_variable_logging") { // Test ATLAS_DEBUG_VAR with multiple ranks logging concurrently const auto& comm = mpi::comm(); const int rank = comm.rank(); const int size = comm.size(); Log::info() << "[Rank " << rank << "] Starting variable debug test" << std::endl; // Each rank logs its rank and size - creates concurrent logging ATLAS_DEBUG_VAR(rank); ATLAS_DEBUG_VAR(size); Log::info() << "[Rank " << rank << "] Variable debug test completed" << std::endl; mpi::comm().barrier(); } CASE("test_debug_sync_basic") { // Test ATLAS_DEBUG_SYNC which is designed for barrier synchronization // The macro itself provides synchronization, so concurrent calls // from all ranks should not cause races. const auto& comm = mpi::comm(); const int rank = comm.rank(); Log::info() << "[Rank " << rank << "] Starting sync debug test" << std::endl; // All ranks call ATLAS_DEBUG_SYNC simultaneously // This barrier ensures synchronization and tests buffer flushing ATLAS_DEBUG_SYNC(); Log::info() << "[Rank " << rank << "] Post-sync logging" << std::endl; mpi::comm().barrier(); } CASE("test_debug_sync_with_message") { // Test ATLAS_DEBUG_SYNC with message parameter from multiple ranks const auto& comm = mpi::comm(); const int rank = comm.rank(); Log::info() << "[Rank " << rank << "] Starting sync with message test" << std::endl; // All ranks call ATLAS_DEBUG_SYNC with a message std::ostringstream oss; oss << "rank_" << rank << "_message"; ATLAS_DEBUG_SYNC(oss.str()); Log::info() << "[Rank " << rank << "] Sync with message test completed" << std::endl; mpi::comm().barrier(); } CASE("test_debug_sync_with_communicator") { // Test ATLAS_DEBUG_SYNC with explicit communicator const auto& comm = mpi::comm(); const int rank = comm.rank(); Log::info() << "[Rank " << rank << "] Starting sync with comm test" << std::endl; // All ranks call ATLAS_DEBUG_SYNC with explicit communicator ATLAS_DEBUG_SYNC(comm); Log::info() << "[Rank " << rank << "] Sync with comm test completed" << std::endl; mpi::comm().barrier(); } CASE("test_debug_sync_with_communicator_and_message") { // Test ATLAS_DEBUG_SYNC with both communicator and message parameters // This is the most complex case and would be most prone to logging races const auto& comm = mpi::comm(); const int rank = comm.rank(); Log::info() << "[Rank " << rank << "] Starting sync with comm and message test" << std::endl; // All ranks call ATLAS_DEBUG_SYNC with both parameters std::ostringstream oss; oss << "concurrent_sync_rank_" << rank; ATLAS_DEBUG_SYNC(comm, oss.str()); Log::info() << "[Rank " << rank << "] Sync with comm and message test completed" << std::endl; mpi::comm().barrier(); } CASE("test_debug_concurrent_multiple_calls") { // Test multiple concurrent ATLAS_DEBUG calls from all ranks // This creates higher contention on the logging system and tests buffer flushing const auto& comm = mpi::comm(); const int rank = comm.rank(); for (int iteration = 0; iteration < 3; ++iteration) { std::ostringstream oss; oss << "concurrent_iteration_" << iteration << "_rank_" << rank; // All ranks simultaneously call ATLAS_DEBUG ATLAS_DEBUG(oss.str()); // Brief local work to simulate realistic MPI application behavior Log::info() << "[Rank " << rank << "] Iteration " << iteration << " work" << std::endl; } Log::info() << "[Rank " << rank << "] Concurrent test completed" << std::endl; mpi::comm().barrier(); } CASE("test_debug_split_communicator") { // Test ATLAS_DEBUG_SYNC with a split communicator to verify it works // with non-default MPI communicators const auto& world_comm = mpi::comm(); const int rank = world_comm.rank(); // Split communicator by rank parity (even/odd) int color = rank % 2; mpi::comm().split(color, "split_" + std::to_string(color)); const auto& split_comm = mpi::comm("split_" + std::to_string(color)); const int local_rank = split_comm.rank(); const int local_size = split_comm.size(); Log::info() << "[WorldRank " << rank << ", LocalRank " << local_rank << "/" << local_size << "] Starting split comm test" << std::endl; // Call ATLAS_DEBUG_SYNC with the split communicator ATLAS_DEBUG_SYNC(split_comm); Log::info() << "[WorldRank " << rank << ", LocalRank " << local_rank << "] Split comm test completed" << std::endl; mpi::comm().barrier(); } CASE("test_debug_sync_var_basic") { // Test ATLAS_DEBUG_SYNC_VAR with just a variable (default communicator) const int rank = mpi::comm().rank(); Log::info() << "[Rank " << rank << "] Starting sync var test" << std::endl; // All ranks call ATLAS_DEBUG_SYNC_VAR with their rank variable ATLAS_DEBUG_SYNC_VAR(rank); Log::info() << "[Rank " << rank << "] Sync var test completed" << std::endl; mpi::comm().barrier(); } CASE("test_debug_sync_var_with_communicator") { // Test ATLAS_DEBUG_SYNC_VAR with explicit communicator const auto& comm = mpi::comm(); const int rank = comm.rank(); const int size = comm.size(); Log::info() << "[Rank " << rank << "] Starting sync var with comm test" << std::endl; // All ranks call ATLAS_DEBUG_SYNC_VAR with variable and communicator ATLAS_DEBUG_SYNC_VAR(comm, size); Log::info() << "[Rank " << rank << "] Sync var with comm test completed" << std::endl; mpi::comm().barrier(); } CASE("test_debug_sync_var_concurrent_multiple_calls") { // Test multiple concurrent ATLAS_DEBUG_SYNC_VAR calls to verify robustness const auto& comm = mpi::comm(); const int rank = comm.rank(); for (int iteration = 0; iteration < 3; ++iteration) { int value = rank * 10 + iteration; // All ranks simultaneously call ATLAS_DEBUG_SYNC_VAR ATLAS_DEBUG_SYNC_VAR(comm, value); Log::info() << "[Rank " << rank << "] Iteration " << iteration << " completed" << std::endl; } Log::info() << "[Rank " << rank << "] Concurrent sync var test completed" << std::endl; mpi::comm().barrier(); } CASE("test_debug_sync_timeout_throws_when_configured") { const auto& comm = mpi::comm(); if (comm.size() == 1) { Log::warning() << "Skipping this test which is designed for multiple ranks." << std::endl; return; } const std::string timeout_action = eckit::Resource("$ATLAS_MPI_BARRIER_TIMEOUT_ACTION", "THROW"); ATLAS_DEBUG_VAR(timeout_action); if (timeout_action != "THROW") { Log::warning() << "Skipping this test since ATLAS_MPI_BARRIER_TIMEOUT_ACTION=" << timeout_action << " is not set to THROW." << std::endl; return; } mpi::comm().split(0, "work_comm"); // before forcing the timeout const auto& work_comm = mpi::comm("work_comm"); const int rank = comm.rank(); const double timeout_seconds = eckit::Resource("$ATLAS_MPI_BARRIER_TIMEOUT", 3.0); bool did_throw = false; if (rank > 0) { // Sleep longer than the barrier timeout to ensure rank 0 times out int sleep_ms = static_cast((timeout_seconds + 1.) * 1000); std::this_thread::sleep_for( std::chrono::milliseconds(sleep_ms)); } try { ATLAS_DEBUG_SYNC(work_comm, "forced timeout throw"); // The internal barrier must be on a separate (work_comm) if we are expected to recover } catch (const eckit::Exception& exception) { did_throw = true; EXPECT(std::string(exception.what()).find("ATLAS_MPI_BARRIER_TIMEOUT") != std::string::npos); Log::info() << "Caught expected exception on rank " << rank << ": " << exception.what() << std::endl; } mpi::comm().barrier(); EXPECT(did_throw == (rank == 0)); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } ecmwf-atlas-b0f79ee/src/tests/runtime/fctest_trace.fypp0000664000175000017500000000402715263665111023577 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/runtime/test_library_noinit_final.cc0000664000175000017500000000176315263665111026000 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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; } ecmwf-atlas-b0f79ee/src/tests/runtime/test_trace.cc0000664000175000017500000000577215263665111022705 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/runtime/test_library_init_nofinal.cc0000664000175000017500000000127615263665111025777 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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; } ecmwf-atlas-b0f79ee/src/tests/acceptance_tests/0000775000175000017500000000000015263665111022053 5ustar alastairalastairecmwf-atlas-b0f79ee/src/tests/acceptance_tests/compare.cmake0000664000175000017500000000201615263665111024502 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() ecmwf-atlas-b0f79ee/src/tests/acceptance_tests/CMakeLists.txt0000664000175000017500000000563115263665111024620 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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() ecmwf-atlas-b0f79ee/src/tests/acceptance_tests/atest_mgrids.cc0000664000175000017500000002063415263665111025054 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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(); } ecmwf-atlas-b0f79ee/src/tests/numerics/0000775000175000017500000000000015263665111020370 5ustar alastairalastairecmwf-atlas-b0f79ee/src/tests/numerics/fctest_ifs_setup.F900000664000175000017500000000712615263665111024227 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/numerics/CMakeLists.txt0000664000175000017500000000304615263665111023133 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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() ecmwf-atlas-b0f79ee/src/tests/numerics/test_fvm_nabla_validation.cc0000664000175000017500000002336715263665111026110 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/numerics/fctest_fvm_nabla.F900000664000175000017500000002755115263665111024157 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/numerics/test_fvm_nabla.cc0000664000175000017500000004371515263665111023675 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/trans/0000775000175000017500000000000015263665111017672 5ustar alastairalastairecmwf-atlas-b0f79ee/src/tests/trans/test_trans.cc0000664000175000017500000006336515263665111022404 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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.spectral(); 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); } ecmwf-atlas-b0f79ee/src/tests/trans/fctest_trans.F900000664000175000017500000003653115263665111022661 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/trans/CMakeLists.txt0000664000175000017500000000663215263665111022441 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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} ) ecbuild_add_test( TARGET atlas_test_trans_adjoints SOURCES test_trans_adjoints.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 ) ecmwf-atlas-b0f79ee/src/tests/trans/fctest_trans_unstructured.F900000664000175000017500000000565115263665111025507 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/trans/test_trans_invtrans_grad.cc0000664000175000017500000001536715263665111025324 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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.spectral(); 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); } ecmwf-atlas-b0f79ee/src/tests/trans/test_trans_localcache.cc0000664000175000017500000003262415263665111024534 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/filesystem/PathName.h" #include "eckit/utils/MD5.h" #include "atlas/grid.h" #include "atlas/option.h" #include "atlas/runtime/Trace.h" #include "atlas/trans/LegendreCacheCreator.h" #include "atlas/trans/Trans.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); } ecmwf-atlas-b0f79ee/src/tests/trans/test_trans_adjoints.cc0000664000175000017500000003107115263665111024264 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 "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/functionspace/Spectral.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Grid.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/trans/Trans.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/function/VortexRollup.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { /// Dot Products ----------------------------------------------------------- // Dot product of 1D grid point fields double dotProd1DGridPoint(const Field& a, const Field& b) { auto ghost = array::make_view(a.functionspace().ghost()); 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) { if (ghost(i)) { continue; } prod += aView(i) * bView(i); } mpi::comm().allReduceInPlace(prod, eckit::mpi::Operation::SUM); return prod; } // Dot product of 2D grid point fields double dotProd2DGridPoint(const Field& a, const Field& b) { auto ghost = array::make_view(a.functionspace().ghost()); double prod{}; const auto aView = array::make_view(a); const auto bView = array::make_view(b); for (idx_t i = 0; i < a.shape(0); ++i) { if (ghost(i)) { continue; } for (idx_t j = 0; j < a.shape(1); ++j) { prod += aView(i, j) * bView(i, j); } } mpi::comm().allReduceInPlace(prod, eckit::mpi::Operation::SUM); return prod; } // Dot product of spectral fields double dotProdSpectral(const Field& a, const Field& b) { /* Because the input fields are real-valued in the spatial domain, * their spectral coefficients exhibit conjugate symmetry: F(n, -m) = (-1)^m * F(n, m)^*. * * To optimize storage, the layout omits negative azimuthal orders (m < 0). * To reconstruct the full global energy integral from this half-spectrum: * - Zonal modes (m = 0) are counted once (weight = 1.0). * - Non-zonal modes (m > 0) are doubled (weight = 2.0) to account for the missing * negative m counterparts. */ double prod{}; const auto aView = array::make_view(a); const auto bView = array::make_view(b); functionspace::Spectral spectral = functionspace::Spectral{a.functionspace()}; ATLAS_ASSERT(spectral); const auto zonal_wavenumbers = spectral.zonal_wavenumbers(); const int truncation = spectral.truncation(); idx_t index = 0; for( idx_t jm=0; jm(option::name("x")); { const array::ArrayView lonlat = array::make_view(gaussFunctionSpace.lonlat()); array::ArrayView view = array::make_view(gaussField); for (idx_t i = 0; i < gaussFunctionSpace.size(); ++i) { view(i) = util::function::vortex_rollup(lonlat(i, LON), lonlat(i, LAT), 1.); } } return gaussField; } // Vortex rollup for 2D wind field Field createVortexRollupWinds(const FunctionSpace& gaussFunctionSpace) { Field windField = gaussFunctionSpace.createField(option::name("x_winds") | option::variables(2)); { const array::ArrayView lonlat = array::make_view(gaussFunctionSpace.lonlat()); array::ArrayView view = array::make_view(windField); for (idx_t i = 0; i < gaussFunctionSpace.size(); ++i) { view(i, 0) = util::function::vortex_rollup(lonlat(i, LON), lonlat(i, LAT), 1.); view(i, 1) = util::function::vortex_rollup(lonlat(i, LON), lonlat(i, LAT), 1.); } } return windField; } /// Test Fixture -------------------------------------------- struct testFixture { functionspace::StructuredColumns gaussFunctionSpace; functionspace::Spectral spectralFunctionSpace; trans::Trans trans_; }; testFixture createTestFixture(const GaussianGrid& gaussGrid) { testFixture fixture; fixture.gaussFunctionSpace = functionspace::StructuredColumns(gaussGrid); fixture.spectralFunctionSpace = functionspace::Spectral(gaussGrid.N()-1); fixture.trans_ = trans::Trans(fixture.gaussFunctionSpace, fixture.spectralFunctionSpace); return fixture; } /// Main test functions ------------------------------------------------------ // Test dirtrans and invtrans adjoints void testDirtrans(const testFixture& testFixture) { // x: Gauss field Field gaussField = createVortexRollup(testFixture.gaussFunctionSpace); /// Test dirtrans (T) ---------------------------- // y = Tx: Spectral field constructed from Gauss field and computed Field spectralField = testFixture.spectralFunctionSpace.createField(option::name("y")); testFixture.trans_.dirtrans(gaussField, spectralField); const double yDotY = dotProd(spectralField, spectralField); // T*Tx: Adjoint spectral field constructed and computed Field adjointSpectralField = testFixture.gaussFunctionSpace.createField(option::name("T*y")); testFixture.trans_.dirtrans_adj(spectralField, adjointSpectralField); const double xDotAdjY = dotProd(gaussField, adjointSpectralField); // Adjoint test = Log::error() << "dirtrans test: =" << yDotY << ", =" << xDotAdjY << std::endl; EXPECT_APPROX_EQ(yDotY / xDotAdjY, 1., 1e-12); //// Test invtrans (T) --------------------------- // x = Ty: Gauss field constructed from spectral field and computed Field secondGaussField = testFixture.gaussFunctionSpace.createField(option::name("x")); testFixture.trans_.invtrans(spectralField, secondGaussField); const double xDotX = dotProd(secondGaussField,secondGaussField); // T*x: Adjoint Gauss field constructed and computed. Field adjointGaussField = testFixture.spectralFunctionSpace.createField(option::name("T*x")); testFixture.trans_.invtrans_adj(secondGaussField, adjointGaussField); const double AdjXDotY = dotProd(adjointGaussField, spectralField); // Adjoint test = Log::error() << "invtrans test: =" << xDotX << ", =" << AdjXDotY << std::endl; EXPECT_APPROX_EQ(xDotX / AdjXDotY, 1., 1e-12); } // Test dirtrans_wind2vordiv and invtrans_wind2vordiv adjoints void testWindVorDiv(const testFixture& testFixture) { // x: Gauss wind field (u,v) Field gaussWindField = createVortexRollupWinds(testFixture.gaussFunctionSpace); /// Test dirtrans_wind2vordiv (T) ---------------------------- // y = Tx (u,v): Spectral fields y = (vor,div) constructed from Gauss wind and = + computed Field vor = testFixture.spectralFunctionSpace.createField(option::name("vor")); Field div = testFixture.spectralFunctionSpace.createField(option::name("div")); testFixture.trans_.dirtrans_wind2vordiv(gaussWindField, vor, div); const double yDotY = dotProd(vor, vor) + dotProd(div, div); // T*y: Adjoint spectral field constructed and computed. Field adjointSpectralField = testFixture.gaussFunctionSpace.createField(option::name("T*y") | option::variables(2)); testFixture.trans_.dirtrans_wind2vordiv_adj(vor, div, adjointSpectralField); const double xDotAdjY = dotProd(gaussWindField, adjointSpectralField); // Adjoint test = Log::error() << "dirtrans_wind2vordiv test: =" << yDotY << ", =" << xDotAdjY << std::endl; EXPECT_APPROX_EQ(yDotY / xDotAdjY, 1., 1e-12); /// Test invtrans_wind2vordiv (T) ---------------------------- // x = Ty: Gauss wind field (u,v) constructed from spectral field and computed Field secondGaussWindField = testFixture.gaussFunctionSpace.createField(option::name("x_winds") | option::variables(2)); testFixture.trans_.invtrans_vordiv2wind(vor, div, secondGaussWindField); const double xDotX = dotProd(secondGaussWindField, secondGaussWindField); // T*x: Adjoint Gauss fields (div,vor) constructed from second Gauss wind field and computed. Field adjointGaussFieldVor = testFixture.spectralFunctionSpace.createField(option::name("T*x_vor")); Field adjointGaussFieldDiv = testFixture.spectralFunctionSpace.createField(option::name("T*x_div")); testFixture.trans_.invtrans_vordiv2wind_adj(secondGaussWindField, adjointGaussFieldVor, adjointGaussFieldDiv); const double AdjXDotY = dotProd(adjointGaussFieldVor, vor) + dotProd(adjointGaussFieldDiv, div); // Adjoint test = Log::error() << "invtrans_wind2vordiv test: =" << xDotX << ", =" << AdjXDotY << std::endl; EXPECT_APPROX_EQ(xDotX / AdjXDotY, 1., 1e-12); } // Test invtrans_grad adjoint void testInvtransGrad(const testFixture& testFixture) { // y: Spectral field from vortex rollup of Gauss field Field gaussField = createVortexRollup(testFixture.gaussFunctionSpace); Field spectralField = testFixture.spectralFunctionSpace.createField(option::name("y")); testFixture.trans_.dirtrans(gaussField, spectralField); // x = Ty: Gauss gradient field (d/dlambda, d/dphi) constructed from spectral field and computed. Field gradField = testFixture.gaussFunctionSpace.createField(option::name("grad") | option::variables(2)); array::make_view(gradField).assign(0.); testFixture.trans_.invtrans_grad(spectralField, gradField); const double xDotX = dotProd(gradField, gradField); // T*x: Adjoint spectral field constructed from Gauss gradient field and computed. Field adjointSpectralField = testFixture.spectralFunctionSpace.createField(option::name("T*x")); array::make_view(adjointSpectralField).assign(0.); testFixture.trans_.invtrans_grad_adj(gradField, adjointSpectralField); const double adjXDotY = dotProd(adjointSpectralField, spectralField); // Adjoint test = Log::info() << "invtrans_grad test: =" << xDotX << ", =" << adjXDotY << std::endl; EXPECT_APPROX_EQ(xDotX / adjXDotY, 1., 1e-12); } // Create test fixture and run all adjoint tests for given grid void testFunction(const GaussianGrid& gaussGrid) { testFixture fixture = createTestFixture(Grid(gaussGrid)); testDirtrans(fixture); testWindVorDiv(fixture); testInvtransGrad(fixture); } /// Test cases --------------------------------------------------------------- CASE("O15") { testFunction(Grid("O15")); } CASE("F15") { testFunction(Grid("F15")); } CASE("O8") { testFunction(Grid("O8")); } CASE("F8") { testFunction(Grid("F8")); } CASE("O12") { testFunction(Grid("O12")); } CASE("F12") { testFunction(Grid("F12")); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } ecmwf-atlas-b0f79ee/src/tests/trans/test_trans_split_comm.cc0000664000175000017500000000667415263665111024632 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); } ecmwf-atlas-b0f79ee/src/tests/trans/test_transgeneral.cc0000664000175000017500000025610015263665111023731 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } } } } CASE("invtrans_vordiv2wind supports rank-1 and rank-2 spectral fields") { constexpr int truncation = 3; constexpr int levels = 2; const Grid grid("F2"); const functionspace::Spectral spectral(truncation, option::levels(levels)); const functionspace::StructuredColumns gridpoints(grid, option::levels(levels)); const trans::Trans trans(gridpoints, spectral); const idx_t spectral_coefficients = trans.spectralCoefficients(); Field spvor = spectral.createField(option::name("vorticity")); Field spdiv = spectral.createField(option::name("divergence")); Field wind = gridpoints.createField(option::name("wind") | option::variables(2)); auto spvor_view = array::make_view(spvor); auto spdiv_view = array::make_view(spdiv); auto wind_view = array::make_view(wind); spvor_view.assign(0.); spdiv_view.assign(0.); spvor_view(8, 0) = 1.; spdiv_view(10, 0) = 0.5; spvor_view(12, 1) = -0.75; spdiv_view(14, 1) = 1.25; trans.invtrans_vordiv2wind(spvor, spdiv, wind); const functionspace::Spectral spectral_level(truncation); const functionspace::StructuredColumns gridpoints_level(grid); const trans::Trans trans_level(gridpoints_level, spectral_level); for (int level = 0; level < levels; ++level) { Field spvor_level = spectral_level.createField(option::name("vorticity")); Field spdiv_level = spectral_level.createField(option::name("divergence")); Field wind_level = gridpoints_level.createField(option::name("wind") | option::variables(2)); auto spvor_level_view = array::make_view(spvor_level); auto spdiv_level_view = array::make_view(spdiv_level); auto wind_level_view = array::make_view(wind_level); for (idx_t coefficient = 0; coefficient < spectral_coefficients; ++coefficient) { spvor_level_view(coefficient) = spvor_view(coefficient, level); spdiv_level_view(coefficient) = spdiv_view(coefficient, level); } trans.invtrans_vordiv2wind(spvor_level, spdiv_level, wind_level); for (idx_t point = 0; point < gridpoints.size(); ++point) { for (int component = 0; component < 2; ++component) { EXPECT_APPROX_EQ(wind_view(point, level, component), wind_level_view(point, component), 1.e-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); } ecmwf-atlas-b0f79ee/src/tests/trans/fctest_trans_invtrans_grad.F900000664000175000017500000001023315263665111025571 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/util/0000775000175000017500000000000015263665111017520 5ustar alastairalastairecmwf-atlas-b0f79ee/src/tests/util/test_flags.cc0000664000175000017500000000253015263665111022162 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/util/test_util.cc0000664000175000017500000000262715263665111022052 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/util/fctest_kdtree.F900000664000175000017500000002607115263665111022634 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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%c_ptr()) ! 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%c_ptr()) ! 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%c_ptr()) ! 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%c_ptr()) ! 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 ecmwf-atlas-b0f79ee/src/tests/util/test_convexsphericalmeanvalue.cc0000664000175000017500000002422415263665111026165 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); }ecmwf-atlas-b0f79ee/src/tests/util/fctest_functions.F900000664000175000017500000000675515263665111023375 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/util/fctest_geometry.F900000664000175000017500000001765215263665111023216 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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%c_ptr()) ! 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%c_ptr()) ! 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%c_ptr()) ! 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 ecmwf-atlas-b0f79ee/src/tests/util/CMakeLists.txt0000664000175000017500000000575715263665111022276 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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 checksum ) 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_spherical_harmonics SOURCES test_spherical_harmonics.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} ) ecmwf-atlas-b0f79ee/src/tests/util/fctest_metadata.F900000664000175000017500000000646115263665111023137 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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%c_ptr()) 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 ecmwf-atlas-b0f79ee/src/tests/util/test_spherical_harmonics.cc0000664000175000017500000001066015263665111025106 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/function/SphericalHarmonic.h" #include #include #include #include #include "tests/AtlasTestEnvironment.h" namespace atlas::test { namespace { double expected(int n, int m, double lon, double lat) { constexpr double pi = M_PI; lon *= pi / 180.; lat *= pi / 180.; if (n == 0 && m == 0) { return 1. / std::sqrt(4. * pi); } if (n == 1 && m == 0) { return std::sqrt(3. / (4. * pi)) * std::sin(lat); } if (n == 1 && m == 1) { return -std::sqrt(3. / (4. * pi)) * std::cos(lat) * std::cos(lon); } if (n == 1 && m == -1) { return -std::sqrt(3. / (4. * pi)) * std::cos(lat) * std::sin(lon); } if (n == 2 && m == 1) { return -3. * std::sqrt(5. / (12. * pi)) * std::sin(lat) * std::cos(lat) * std::cos(lon); } if (n == 2 && m == -1) { return -3. * std::sqrt(5. / (12. * pi)) * std::sin(lat) * std::cos(lat) * std::sin(lon); } ATLAS_NOTIMPLEMENTED; } void expect_harmonic(int n, int m, double lon, double lat) { const double value = expected(n, m, lon, lat); EXPECT_APPROX_EQ(util::function::spherical_harmonic(n, m, lon, lat), value, 1.e-14); EXPECT_APPROX_EQ(util::function::SphericalHarmonic(n, m)(lon, lat), value, 1.e-14); } } // namespace CASE("spherical harmonic function and class match analytic values") { expect_harmonic(0, 0, 0., 0.); expect_harmonic(1, 0, 45., 30.); expect_harmonic(1, 1, 35., -20.); expect_harmonic(1, -1, 35., -20.); expect_harmonic(2, 1, -70., 25.); expect_harmonic(2, -1, -70., 25.); } CASE("spherical harmonic caches handle latitude reuse and pair changes") { const util::function::SphericalHarmonic first(1, 1); const util::function::SphericalHarmonic second(2, 1); for (const double lat : {20., -35., 20.}) { EXPECT_APPROX_EQ(first(40., lat), expected(1, 1, 40., lat), 1.e-14); EXPECT_APPROX_EQ(util::function::spherical_harmonic(1, 1, 40., lat), expected(1, 1, 40., lat), 1.e-14); } for (int iteration = 0; iteration < 3; ++iteration) { EXPECT_APPROX_EQ(first(40., 20.), expected(1, 1, 40., 20.), 1.e-14); EXPECT_APPROX_EQ(second(40., 20.), expected(2, 1, 40., 20.), 1.e-14); EXPECT_APPROX_EQ(first(40., 20.), expected(1, 1, 40., 20.), 1.e-14); } } CASE("spherical harmonic rejects invalid wave numbers") { EXPECT_THROWS(util::function::spherical_harmonic(2, 3, 0., 0.)); EXPECT_THROWS(util::function::SphericalHarmonic(2, 3)); } CASE("spherical harmonic caches are thread safe") { constexpr int thread_count = 8; const util::function::SphericalHarmonic first(1, 1); const util::function::SphericalHarmonic second(2, -1); std::atomic start{false}; std::vector failures(thread_count, 0); std::vector threads; threads.reserve(thread_count); for (int thread = 0; thread < thread_count; ++thread) { threads.emplace_back([thread, &first, &second, &start, &failures]() { while (!start.load(std::memory_order_acquire)) { std::this_thread::yield(); } for (int iteration = 0; iteration < 100; ++iteration) { const double lon = -150. + 7. * thread + iteration; const double lat = -60. + 5. * thread + (iteration % 5) * 11.; failures[thread] += std::abs(first(lon, lat) - expected(1, 1, lon, lat)) > 1.e-14; failures[thread] += std::abs(second(lon, lat) - expected(2, -1, lon, lat)) > 1.e-14; failures[thread] += std::abs(util::function::spherical_harmonic(1, -1, lon, lat) - expected(1, -1, lon, lat)) > 1.e-14; } }); } start.store(true, std::memory_order_release); for (auto& thread : threads) { thread.join(); } for (const int failure_count : failures) { EXPECT_EQ(failure_count, 0); } } } // namespace atlas::test int main(int argc, char** argv) { return atlas::test::run(argc, argv); }ecmwf-atlas-b0f79ee/src/tests/util/fctest_parametrisation.F900000664000175000017500000001543215263665111024557 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/util/fctest_error.F900000664000175000017500000000524015263665111022502 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/util/test_polygon.cc0000664000175000017500000000654615263665111022570 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/util/test_convexsphericalpolygon.cc0000664000175000017500000007204115263665111025677 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); } ecmwf-atlas-b0f79ee/src/tests/util/test_unitsphere.cc0000664000175000017500000000357015263665111023261 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); } ecmwf-atlas-b0f79ee/src/tests/util/fctest_logging.F900000664000175000017500000000501015263665111022772 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/tests/util/test_earth.cc0000664000175000017500000001220715263665111022173 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/util/test_checksum.cc0000664000175000017500000001571115263665111022675 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/Checksum.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { namespace { template util::checksum_t streamed_checksum(const std::vector& values, std::size_t split1, std::size_t split2) { util::checksum_t checksum{}; util::checksum_reset(checksum); util::checksum_update(checksum, values.data(), split1); util::checksum_update(checksum, values.data() + split1, split2); util::checksum_update(checksum, values.data() + split1 + split2, values.size() - split1 - split2); return util::checksum_digest(checksum); } template void expect_streaming_matches_oneshot(const std::vector& values, std::size_t split1, std::size_t split2) { EXPECT_EQ(util::checksum(values.data(), values.size()), streamed_checksum(values, split1, split2)); } template void expect_zero_length_is_supported() { std::vector values; util::checksum_t checksum{}; util::checksum_reset(checksum); util::checksum_update(checksum, values.data(), values.size()); EXPECT_EQ(util::checksum(values.data(), values.size()), 0ul); EXPECT_EQ(util::checksum_digest(checksum), 0ul); } template void expect_strided_matches_selected(const std::vector& values, std::size_t count, std::size_t stride) { std::vector selected(count); for (std::size_t i = 0; i < count; ++i) { selected[i] = values[i * stride]; } util::checksum_t checksum{}; util::checksum_reset(checksum); util::checksum_update(checksum, values.data(), count, stride); EXPECT_EQ(util::checksum_digest(checksum), util::checksum(selected.data(), selected.size())); } template void expect_zero_length_strided_is_supported(const std::vector& values, std::size_t stride) { util::checksum_t checksum{}; util::checksum_reset(checksum); util::checksum_update(checksum, values.data(), 0, stride); EXPECT_EQ(util::checksum_digest(checksum), 0ul); } } // namespace CASE("checksum streaming matches one-shot for int") { std::vector values(257); for (std::size_t i = 0; i < values.size(); ++i) { values[i] = static_cast(i * 17 + 3); } expect_streaming_matches_oneshot(values, 13, 101); } CASE("checksum streaming matches one-shot for long") { std::vector values(129); for (std::size_t i = 0; i < values.size(); ++i) { values[i] = static_cast(i * 101 + 7); } expect_streaming_matches_oneshot(values, 17, 53); } CASE("checksum streaming matches one-shot for float") { std::vector values(73); for (std::size_t i = 0; i < values.size(); ++i) { values[i] = 0.25f * static_cast(i + 1) * static_cast(i + 5); } expect_streaming_matches_oneshot(values, 11, 29); } CASE("checksum streaming matches one-shot for double") { std::vector values(37); for (std::size_t i = 0; i < values.size(); ++i) { values[i] = 0.5 * static_cast(i + 1) * static_cast(i + 3); } expect_streaming_matches_oneshot(values, 5, 11); } CASE("checksum streaming matches one-shot for checksum_t") { std::vector values(19); for (std::size_t i = 0; i < values.size(); ++i) { values[i] = static_cast(i * 1009u + 11u); } expect_streaming_matches_oneshot(values, 4, 7); } CASE("checksum supports zero-length int input") { expect_zero_length_is_supported(); } CASE("checksum supports zero-length long input") { expect_zero_length_is_supported(); } CASE("checksum supports zero-length float input") { expect_zero_length_is_supported(); } CASE("checksum supports zero-length double input") { expect_zero_length_is_supported(); } CASE("checksum supports zero-length checksum_t input") { expect_zero_length_is_supported(); } CASE("checksum strided update matches selected values for int") { std::vector values(300); for (std::size_t i = 0; i < values.size(); ++i) { values[i] = static_cast(i * 13 + 5); } expect_strided_matches_selected(values, 73, 3); expect_strided_matches_selected(values, 100, 1); expect_zero_length_strided_is_supported(values, 7); } CASE("checksum strided update matches selected values for long") { std::vector values(260); for (std::size_t i = 0; i < values.size(); ++i) { values[i] = static_cast(i * 71 + 17); } expect_strided_matches_selected(values, 52, 5); expect_strided_matches_selected(values, 87, 1); expect_zero_length_strided_is_supported(values, 9); } CASE("checksum strided update matches selected values for float") { std::vector values(210); for (std::size_t i = 0; i < values.size(); ++i) { values[i] = 0.125f * static_cast((i + 2) * (i + 7)); } expect_strided_matches_selected(values, 42, 4); expect_strided_matches_selected(values, 64, 1); expect_zero_length_strided_is_supported(values, 11); } CASE("checksum strided update matches selected values for double") { std::vector values(205); for (std::size_t i = 0; i < values.size(); ++i) { values[i] = 0.0625 * static_cast((i + 3) * (i + 9)); } expect_strided_matches_selected(values, 41, 5); expect_strided_matches_selected(values, 70, 1); expect_zero_length_strided_is_supported(values, 13); } CASE("checksum strided update matches selected values for checksum_t") { std::vector values(190); for (std::size_t i = 0; i < values.size(); ++i) { values[i] = static_cast(i * 4099u + 29u); } expect_strided_matches_selected(values, 38, 5); expect_strided_matches_selected(values, 55, 1); expect_zero_length_strided_is_supported(values, 15); } CASE("checksum reset clears state") { std::vector values(32); for (std::size_t i = 0; i < values.size(); ++i) { values[i] = static_cast(i * 9 + 1); } util::checksum_t checksum{}; util::checksum_reset(checksum); util::checksum_update(checksum, values.data(), 10); util::checksum_reset(checksum); util::checksum_update(checksum, values.data(), values.size()); EXPECT_EQ(util::checksum_digest(checksum), util::checksum(values.data(), values.size())); } CASE ("test_vector") { std::vector int_vector = {1, 2}; EXPECT_EQ( util::checksum(int_vector.data(), int_vector.size()), 0x1003); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); }ecmwf-atlas-b0f79ee/src/tests/util/test_kdtree.cc0000664000175000017500000002616715263665111022360 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/util/test_point.cc0000664000175000017500000000377215263665111022230 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/util/test_vector.cc0000664000175000017500000000351415263665111022373 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/util/test_metadata.cc0000664000175000017500000000331515263665111022650 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); } ecmwf-atlas-b0f79ee/src/tests/util/test_pack_vector_fields.cc0000664000175000017500000002636615263665111024731 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); } ecmwf-atlas-b0f79ee/src/atlas_f/0000775000175000017500000000000015263665111017012 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas_f/internals/0000775000175000017500000000000015263665111021011 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas_f/internals/Library.cc0000664000175000017500000000311215263665111022721 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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" ecmwf-atlas-b0f79ee/src/atlas_f/internals/atlas_read_file.h0000664000175000017500000000077115263665111024265 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas_f/internals/Library.h0000664000175000017500000000141215263665111022564 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the 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); } ecmwf-atlas-b0f79ee/src/atlas_f/internals/atlas_read_file.cc0000664000175000017500000000337715263665111024430 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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; } //----------------------------------------------------------------------------- } ecmwf-atlas-b0f79ee/src/atlas_f/internals/atlas_generics.fypp0000664000175000017500000000500715263665111024676 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas_f/output/0000775000175000017500000000000015263665111020352 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas_f/output/atlas_output_module.F900000664000175000017500000001535715263665111024736 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr()) ) 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%c_ptr(),mesh%c_ptr(),config%c_ptr()) else opt_config = atlas_Config() call atlas__output__write_mesh(this%c_ptr(),mesh%c_ptr(),opt_config%c_ptr()) 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%c_ptr(),field%c_ptr(),functionspace%c_ptr(), & config%c_ptr()) else opt_config = atlas_Config() call atlas__output__write_field_fs(this%c_ptr(),field%c_ptr(),functionspace%c_ptr(), & opt_config%c_ptr()) 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%c_ptr(),fieldset%c_ptr(),functionspace%c_ptr(), & config%c_ptr()) else opt_config = atlas_Config() call atlas__output__write_fieldset_fs(this%c_ptr(),fieldset%c_ptr(),functionspace%c_ptr(), & opt_config%c_ptr()) 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%c_ptr(),field%c_ptr(), & config%c_ptr()) else opt_config = atlas_Config() call atlas__output__write_field(this%c_ptr(),field%c_ptr(), & opt_config%c_ptr()) 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%c_ptr(),fieldset%c_ptr(), & config%c_ptr()) else opt_config = atlas_Config() call atlas__output__write_fieldset(this%c_ptr(),fieldset%c_ptr(), & opt_config%c_ptr()) 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 ecmwf-atlas-b0f79ee/src/atlas_f/grid/0000775000175000017500000000000015263665111017737 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas_f/grid/atlas_Partitioner_module.F900000664000175000017500000001054515263665111025255 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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_MatchingPartitioner__ctor end interface interface atlas_MatchingMeshPartitioner module procedure atlas_MatchingPartitioner__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%c_ptr() ) ) 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_MatchingPartitioner__ctor( object, config ) result(this) use atlas_config_module, only : atlas_Config use atlas_partitioner_c_binding type(atlas_Partitioner) :: this class(fckit_owned_object), intent(in) :: object type(atlas_Config), intent(in), optional :: config type(atlas_Config) :: opt_config if( present(config) ) then call this%reset_c_ptr( atlas__grid__MatchingPartitioner__new( & object%c_ptr(), config%c_ptr() ) ) else opt_config = atlas_Config() call this%reset_c_ptr( atlas__grid__MatchingPartitioner__new( & object%c_ptr(), opt_config%c_ptr() ) ) call opt_config%final() endif call this%return() end function atlas_MatchingPartitioner__ctor 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%c_ptr(), grid%c_ptr() ) ) 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 ecmwf-atlas-b0f79ee/src/atlas_f/grid/atlas_Grid_module.F900000664000175000017500000012237615263665111023650 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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 :: type => atlas_Grid__type 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_xy 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_Grid_c_binding type(atlas_Grid) :: this character(len=*), intent(in) :: identifier call this%reset_c_ptr( atlas__grid__Grid(c_str(identifier)) ) call this%return() end function function atlas_Grid__ctor_config(config) result(this) use atlas_grid_Grid_c_binding type(atlas_Grid) :: this type(atlas_Config), intent(in) :: config call this%reset_c_ptr( atlas__grid__Grid__config(config%c_ptr()) ) call this%return() end function function atlas_Grid__ctor_cptr(cptr) result(this) use fckit_c_interop_module, only: c_str 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%c_ptr()) ) 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_UnstructuredGrid__ctor_xy(x, y) result(this) use, intrinsic :: iso_c_binding, only : c_double, c_size_t use atlas_grid_unstructured_c_binding type(atlas_UnstructuredGrid) :: this real(c_double), intent(in) :: x(:) real(c_double), intent(in) :: y(:) integer(c_size_t) :: xstride integer(c_size_t) :: ystride integer(c_size_t) :: n n = size(x, kind=c_size_t) if (n /= size(y, kind=c_size_t)) then error stop 'atlas_UnstructuredGrid(x,y): x and y must have the same size' end if xstride = 1_c_size_t ystride = 1_c_size_t call this%reset_c_ptr( atlas__grid__Unstructured__x_y(n, x, y, xstride, ystride) ) 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%c_ptr()) ) 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%c_ptr() ) ) 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%c_ptr() ) ) 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%c_ptr(), name_c_str, size ) name = c_ptr_to_string(name_c_str) call c_ptr_free(name_c_str) end function function atlas_Grid__type(this) result(type) 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 :: type type(c_ptr) :: type_c_str integer :: size call atlas__grid__Grid__type(this%c_ptr(), type_c_str, size ) type = c_ptr_to_string(type_c_str) call c_ptr_free(type_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%c_ptr()),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%c_ptr()) ) 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%c_ptr(), 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%c_ptr())) 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%c_ptr()) 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%c_ptr()) 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%c_ptr()) 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%c_ptr()) 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr()) == 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%c_ptr(), 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%c_ptr()) 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%c_ptr()) 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), & & 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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 ecmwf-atlas-b0f79ee/src/atlas_f/grid/atlas_Vertical_module.F900000664000175000017500000001073615263665111024530 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr() ) ) 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%c_ptr() ) 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 ecmwf-atlas-b0f79ee/src/atlas_f/grid/atlas_StencilComputer_module.F900000664000175000017500000003370415263665111026077 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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 ecmwf-atlas-b0f79ee/src/atlas_f/grid/atlas_GridDistribution_module.F900000664000175000017500000001337115263665111026242 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr(), config%c_ptr()) ) 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%c_ptr(), partitioner%c_ptr()) ) 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr()) 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 ecmwf-atlas-b0f79ee/src/atlas_f/parallel/0000775000175000017500000000000015263665111020606 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas_f/parallel/atlas_Checksum_module.fypp0000664000175000017500000001440415263665111026004 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr()) 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), & & 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%c_ptr(), & & 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%c_ptr(), & & 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 ecmwf-atlas-b0f79ee/src/atlas_f/parallel/atlas_HaloExchange_module.fypp0000664000175000017500000001321515263665111026567 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr()) 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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 ecmwf-atlas-b0f79ee/src/atlas_f/parallel/atlas_GatherScatter_module.fypp0000664000175000017500000002470215263665111027004 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr()) 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%c_ptr(), 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%c_ptr(), 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%c_ptr()) 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%c_ptr(), & & 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%c_ptr(), & & 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%c_ptr(), & & 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%c_ptr(), & & 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%c_ptr(), & & 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%c_ptr(), & & 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 ecmwf-atlas-b0f79ee/src/atlas_f/functionspace/0000775000175000017500000000000015263665111021653 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas_f/functionspace/atlas_functionspace_EdgeColumns_module.F900000664000175000017500000002302115263665111032010 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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, 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%c_ptr(),config%c_ptr()) ) 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%c_ptr()) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) ! 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%c_ptr()) ) ! 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%c_ptr(), & local%c_ptr(),global%c_ptr()) 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%c_ptr(),local%c_ptr(),global%c_ptr()) 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%c_ptr(), & global%c_ptr(),local%c_ptr()) 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%c_ptr(), & global%c_ptr(),local%c_ptr()) 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%c_ptr()) ) ! call halo_exchange%return() end function !------------------------------------------------------------------------------ function get_checksum(this) result(checksum) use atlas_Deprecation_module, only : atlas_deprecation_errors, atlas_deprecation_warnings use atlas_functionspace_EdgeColumns_c_binding type(atlas_Checksum) :: checksum class(atlas_functionspace_EdgeColumns), intent(in) :: this if (atlas_deprecation_warnings()) then call warn_edgecolumns_get_checksum_deprecation_once() end if if (atlas_deprecation_errors()) then write(0, '(A)') "[ATLAS_DEPRECATION_ERROR] atlas_functionspace_EdgeColumns%get_checksum should no longer be used.& & Please use atlas_functionspace_EdgeColumns%checksum instead." write(0, '(A)') "[ATLAS_DEPRECATION_ERROR] This error can be disabled with `export ATLAS_DEPRECATION_ERRORS=0`" error stop end if call checksum%reset_c_ptr( atlas__fs__EdgeColumns__get_checksum(this%c_ptr()) ) end function !------------------------------------------------------------------------------- subroutine warn_edgecolumns_get_checksum_deprecation_once() logical, save :: warned = .false. if (.not. warned) then warned = .true. write(0, '(A)') "[ATLAS_DEPRECATION_WARNING] atlas_functionspace_EdgeColumns%get_checksum should no longer be used.& & Please use atlas_functionspace_EdgeColumns%checksum instead." write(0, '(A)') "[ATLAS_DEPRECATION_WARNING] This warning can be disabled with `export ATLAS_DEPRECATION_WARNINGS=0`" end if end subroutine !------------------------------------------------------------------------------- #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 ecmwf-atlas-b0f79ee/src/atlas_f/functionspace/atlas_functionspace_Spectral_module.F900000664000175000017500000002151715263665111031370 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr()) ) 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%c_ptr(),local%c_ptr(),global%c_ptr()) 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%c_ptr(),global%c_ptr(),local%c_ptr()) 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%c_ptr(),local%c_ptr(),global%c_ptr()) 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%c_ptr(),global%c_ptr(),local%c_ptr()) 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%c_ptr(),field%c_ptr(),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%c_ptr(),field%c_ptr(),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_base1(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 ecmwf-atlas-b0f79ee/src/atlas_f/functionspace/atlas_FunctionSpace_module.fypp0000664000175000017500000003173615263665111030057 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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" 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, public :: lonlat procedure, public :: ghost procedure, public :: global_index procedure, public :: remote_index procedure, public :: partition 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 procedure, private :: checksum_field procedure, private :: checksum_fieldset #:for rank in ranks #:for dtype in dtypes[:4] procedure, private :: checksum_${dtype}$_r${rank}$ #:endfor #:endfor 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 generic, public :: checksum => & & checksum_field, & & checksum_fieldset, & #:set counter = 1 #:set last = len(ranks) * len(dtypes[:4]) #:for rank in ranks #:for dtype in dtypes[:4] & checksum_${dtype}$_r${rank}$#{if counter < last}#, & #{endif}# #:set counter = counter + 1 #:endfor #:endfor #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%c_ptr(), name_c_str, size ) name = c_ptr_to_string(name_c_str) call c_ptr_free(name_c_str) end function function lonlat(this) result(field) use atlas_functionspace_c_binding type(atlas_Field) :: field class(atlas_Functionspace), intent(in) :: this field = atlas_Field( atlas__FunctionSpace__lonlat(this%c_ptr()) ) call field%return() end function function ghost(this) result(field) use atlas_functionspace_c_binding type(atlas_Field) :: field class(atlas_Functionspace), intent(in) :: this field = atlas_Field( atlas__FunctionSpace__ghost(this%c_ptr()) ) call field%return() end function function global_index(this) result(field) use atlas_functionspace_c_binding type(atlas_Field) :: field class(atlas_Functionspace), intent(in) :: this field = atlas_Field( atlas__FunctionSpace__global_index(this%c_ptr()) ) call field%return() end function function remote_index(this) result(field) use atlas_functionspace_c_binding type(atlas_Field) :: field class(atlas_Functionspace), intent(in) :: this field = atlas_Field( atlas__FunctionSpace__remote_index(this%c_ptr()) ) call field%return() end function function partition(this) result(field) use atlas_functionspace_c_binding type(atlas_Field) :: field class(atlas_Functionspace), intent(in) :: this field = atlas_Field( atlas__FunctionSpace__partition(this%c_ptr()) ) call field%return() 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%c_ptr(), options%c_ptr() ) ) 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%c_ptr(), template%c_ptr(),options%c_ptr()) ) 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%c_ptr(),fieldset%c_ptr()) 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%c_ptr(),field%c_ptr()) 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%c_ptr(),fieldset%c_ptr()) 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%c_ptr(),field%c_ptr()) end subroutine !------------------------------------------------------------------------------ function checksum_field(this,field) result(checksum) 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 character(len=:), allocatable :: checksum class(atlas_Functionspace), intent(in) :: this type(atlas_Field), intent(in) :: field type(c_ptr) :: checksum_cptr integer :: checksum_size call atlas__FunctionSpace__checksum_field(this%c_ptr(),field%c_ptr(),checksum_cptr,checksum_size) allocate(character(len=checksum_size) :: checksum) checksum = c_ptr_to_string(checksum_cptr) call c_ptr_free(checksum_cptr) end function checksum_field !------------------------------------------------------------------------------ function checksum_fieldset(this,fieldset) result(checksum) 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 character(len=:), allocatable :: checksum class(atlas_Functionspace), intent(in) :: this type(atlas_FieldSet), intent(in) :: fieldset type(c_ptr) :: checksum_cptr integer :: checksum_size call atlas__FunctionSpace__checksum_fieldset(this%c_ptr(),fieldset%c_ptr(),checksum_cptr,checksum_size) allocate(character(len=checksum_size) :: checksum) checksum = c_ptr_to_string(checksum_cptr) call c_ptr_free(checksum_cptr) end function checksum_fieldset !------------------------------------------------------------------------------ #:for rank in ranks #:for dtype,ftype,ctype in types[:4] function checksum_${dtype}$_r${rank}$(this,array) result(checksum) use, intrinsic :: iso_c_binding, only : c_int, c_long, c_float, c_double character(len=:), allocatable :: checksum class(atlas_Functionspace), intent(in) :: this ${ftype}$, intent(in), target :: array(${dim[rank]}$) type(atlas_Field) :: field field = atlas_Field(array) checksum = checksum_field(this, field) call field%final() end function checksum_${dtype}$_r${rank}$ #:endfor #:endfor !------------------------------------------------------------------------------ ! 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%c_ptr(), options%c_ptr() ) ) 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%c_ptr(), options%c_ptr() ) ) 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 ecmwf-atlas-b0f79ee/src/atlas_f/functionspace/atlas_functionspace_PointCloud_module.F900000664000175000017500000001177715263665111031702 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%c_ptr() ) ) 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%c_ptr(), ghost%c_ptr() ) ) 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%c_ptr() ) ) 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%c_ptr() ) ) 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%c_ptr()) 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%c_ptr()) ) 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 ecmwf-atlas-b0f79ee/src/atlas_f/functionspace/atlas_functionspace_CellColumns_module.F900000664000175000017500000002307415263665111032033 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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, 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%c_ptr(),config%c_ptr()) ) 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%c_ptr()) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr(),local%c_ptr(),global%c_ptr()) 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%c_ptr(),local%c_ptr(),global%c_ptr()) 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%c_ptr(),global%c_ptr(),local%c_ptr()) 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%c_ptr(),global%c_ptr(),local%c_ptr()) 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%c_ptr()) ) end function !------------------------------------------------------------------------------ function get_checksum(this) result(checksum) use atlas_Deprecation_module, only : atlas_deprecation_errors, atlas_deprecation_warnings use atlas_functionspace_CellColumns_c_binding type(atlas_Checksum) :: checksum class(atlas_functionspace_CellColumns), intent(in) :: this if (atlas_deprecation_warnings()) then call warn_cellcolumns_get_checksum_deprecation_once() end if if (atlas_deprecation_errors()) then write(0, '(A)') "[ATLAS_DEPRECATION_ERROR] atlas_functionspace_CellColumns%get_checksum should no longer be used.& & Please use atlas_functionspace_CellColumns%checksum instead." write(0, '(A)') "[ATLAS_DEPRECATION_ERROR] This error can be disabled with `export ATLAS_DEPRECATION_ERRORS=0`" error stop end if call checksum%reset_c_ptr( atlas__CellsFunctioNSpace__get_checksum(this%c_ptr()) ) end function !------------------------------------------------------------------------------ subroutine warn_cellcolumns_get_checksum_deprecation_once() logical, save :: warned = .false. if (.not. warned) then warned = .true. write(0, '(A)') "[ATLAS_DEPRECATION_WARNING] atlas_functionspace_CellColumns%get_checksum should no longer be used.& & Please use atlas_functionspace_CellColumns%checksum instead." write(0, '(A)') "[ATLAS_DEPRECATION_WARNING] This warning can be disabled with `export ATLAS_DEPRECATION_WARNINGS=0`" end if end subroutine !------------------------------------------------------------------------------ #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 ecmwf-atlas-b0f79ee/src/atlas_f/functionspace/atlas_functionspace_BlockStructuredColumns_module.F900000664000175000017500000005211215263665111034266 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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 :: 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, nproma, halo, levels) result(this) use atlas_functionspace_BlockStructuredColumns_c_binding type(atlas_functionspace_BlockStructuredColumns) :: this class(atlas_Grid), intent(in) :: grid integer, optional, intent(in) :: nproma integer, optional, intent(in) :: halo integer, optional, intent(in) :: 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%c_ptr(), & & config%c_ptr() ) ) 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%c_ptr(), & & config%c_ptr() ) ) call this%set_index() call this%return() end function function ctor_grid_dist(grid, distribution, nproma, 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, intent(in) :: halo integer, optional, intent(in) :: nproma integer, optional, intent(in) :: 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(nproma) ) call config%set("nproma",nproma) 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%c_ptr(), distribution%c_ptr(), config%c_ptr() ) ) call this%set_index() call config%final() call this%return() end function function ctor_grid_dist_levels(grid, distribution, levels, nproma, 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, intent(in) :: halo integer, optional, intent(in) :: nproma real(c_double), intent(in) :: 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) if( present(nproma) ) call config%set("nproma",nproma) call config%set("levels",size(levels)) vertical = atlas_Vertical(levels) call this%reset_c_ptr( atlas__functionspace__BStructuredColumns__new__grid_dist_vert( & & grid%c_ptr(), distribution%c_ptr(), vertical%c_ptr(), & & config%c_ptr() ) ) call this%set_index() call config%final() call vertical%final() call this%return() end function function ctor_grid_dist_vertical(grid, distribution, vertical, nproma, 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, intent(in) :: halo integer, optional, intent(in) :: nproma type(atlas_Vertical), intent(in) :: 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) if( present(nproma) ) call config%set("nproma",nproma) call config%set("levels",vertical%size()) call this%reset_c_ptr( atlas__functionspace__BStructuredColumns__new__grid_dist_vert( & & grid%c_ptr(), distribution%c_ptr(), vertical%c_ptr(), & & config%c_ptr() ) ) 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%c_ptr(), distribution%c_ptr(), & & config%c_ptr() ) ) call this%set_index() call this%return() end function function ctor_grid_part(grid, partitioner, halo, nproma, 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, intent(in) :: halo integer, optional, intent(in) :: nproma integer, optional, intent(in) :: 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_part( & & grid%c_ptr(), partitioner%c_ptr(), config%c_ptr() ) ) call this%set_index() call config%final() call this%return() end function function ctor_grid_part_levels(grid, partitioner, levels, nproma, 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, intent(in) :: halo integer, optional, intent(in) :: nproma real(c_double), intent(in) :: 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) if( present(nproma) ) call config%set("nproma",nproma) call config%set("levels",size(levels)) vertical = atlas_Vertical(levels) call this%reset_c_ptr( atlas__functionspace__BStructuredColumns__new__grid_part_vert( & & grid%c_ptr(), partitioner%c_ptr(), vertical%c_ptr(), & & config%c_ptr() ) ) call this%set_index() call config%final() call vertical%final() call this%return() end function function ctor_grid_part_vertical(grid, partitioner, vertical, nproma, 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, intent(in) :: nproma integer, optional, intent(in) :: 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(nproma) ) call config%set("nproma",nproma) 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%c_ptr(), partitioner%c_ptr(), vertical%c_ptr(), & & config%c_ptr() ) ) 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%c_ptr(),local%c_ptr(),global%c_ptr()) 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%c_ptr(),local%c_ptr(),global%c_ptr()) 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%c_ptr(),global%c_ptr(),local%c_ptr()) 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%c_ptr(),global%c_ptr(),local%c_ptr()) end subroutine 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%c_ptr(), 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%c_ptr()) 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%c_ptr()) 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%c_ptr(),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%c_ptr(),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%c_ptr()) 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%c_ptr()) 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%c_ptr(),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%c_ptr(),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%c_ptr()) 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%c_ptr()) 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%c_ptr()) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr(),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%c_ptr(),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%c_ptr()) 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%c_ptr()) 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 ecmwf-atlas-b0f79ee/src/atlas_f/functionspace/atlas_functionspace_StructuredColumns_module.F900000664000175000017500000004412215263665111033315 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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 :: 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%c_ptr(), & & config%c_ptr() ) ) 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%c_ptr(), distribution%c_ptr(), config%c_ptr() ) ) 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%c_ptr(), distribution%c_ptr(), vertical%c_ptr(), & & config%c_ptr() ) ) 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%c_ptr(), distribution%c_ptr(), vertical%c_ptr(), & & config%c_ptr() ) ) 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%c_ptr(), distribution%c_ptr(), & & config%c_ptr() ) ) 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%c_ptr(), partitioner%c_ptr(), config%c_ptr() ) ) 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%c_ptr(), partitioner%c_ptr(), vertical%c_ptr(), & & config%c_ptr() ) ) 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%c_ptr(), partitioner%c_ptr(), vertical%c_ptr(), & & config%c_ptr() ) ) 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%c_ptr(),local%c_ptr(),global%c_ptr()) 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%c_ptr(),local%c_ptr(),global%c_ptr()) 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%c_ptr(),global%c_ptr(),local%c_ptr()) 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%c_ptr(),global%c_ptr(),local%c_ptr()) end subroutine 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%c_ptr(), 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%c_ptr()) 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%c_ptr()) 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%c_ptr(),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%c_ptr(),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%c_ptr()) 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%c_ptr()) 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%c_ptr(),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%c_ptr(),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%c_ptr()) 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%c_ptr()) 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%c_ptr()) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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 ecmwf-atlas-b0f79ee/src/atlas_f/functionspace/atlas_functionspace_NodeColumns_module.fypp0000664000175000017500000007424715263665111032471 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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, 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%c_ptr(),config%c_ptr()) ) 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%c_ptr()) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr(),local%c_ptr(),global%c_ptr()) 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%c_ptr(),local%c_ptr(),global%c_ptr()) 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%c_ptr(),global%c_ptr(),local%c_ptr()) 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%c_ptr(),global%c_ptr(),local%c_ptr()) 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%c_ptr()) ) end function !------------------------------------------------------------------------------ function get_checksum(this) result(checksum) use atlas_Deprecation_module, only : atlas_deprecation_errors, atlas_deprecation_warnings use atlas_functionspace_NodeColumns_c_binding type(atlas_Checksum) :: checksum class(atlas_functionspace_NodeColumns), intent(in) :: this if (atlas_deprecation_warnings()) then call warn_nodecolumns_get_checksum_deprecation_once() end if if (atlas_deprecation_errors()) then write(0, '(A)') "[ATLAS_DEPRECATION_ERROR] atlas_functionspace_NodeColumns%get_checksum should no longer be used.& & Please use atlas_functionspace_NodeColumns%checksum instead." write(0, '(A)') "[ATLAS_DEPRECATION_ERROR] This error can be disabled with `export ATLAS_DEPRECATION_ERRORS=0`" error stop end if call checksum%reset_c_ptr( atlas__NodesFunctioNSpace__get_checksum(this%c_ptr()) ) end function !------------------------------------------------------------------------------ subroutine warn_nodecolumns_get_checksum_deprecation_once() logical, save :: warned = .false. if (.not. warned) then warned = .true. write(0, '(A)') "[ATLAS_DEPRECATION_WARNING] atlas_functionspace_NodeColumns%get_checksum should no longer be used.& & Please use atlas_functionspace_NodeColumns%checksum instead." write(0, '(A)') "[ATLAS_DEPRECATION_WARNING] This warning can be disabled with `export ATLAS_DEPRECATION_WARNINGS=0`" end if end subroutine !------------------------------------------------------------------------------ #: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%c_ptr(),field%c_ptr(),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%c_ptr(),field%c_ptr(),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%c_ptr(),field%c_ptr(),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%c_ptr(),field%c_ptr(),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%c_ptr(),field%c_ptr(),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%c_ptr(),field%c_ptr(),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%c_ptr(),field%c_ptr(),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%c_ptr(),field%c_ptr(),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%c_ptr(),field%c_ptr(),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%c_ptr(),field%c_ptr(),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%c_ptr(),field%c_ptr(),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%c_ptr(),field%c_ptr(),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%c_ptr(),field%c_ptr(),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%c_ptr(),field%c_ptr(),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%c_ptr(),field%c_ptr(),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%c_ptr(),field%c_ptr(),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%c_ptr(),field%c_ptr(),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%c_ptr(),field%c_ptr(),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%c_ptr(),field%c_ptr(),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%c_ptr(),field%c_ptr(),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%c_ptr(),field%c_ptr(),minimum%c_ptr(),location%c_ptr()) 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%c_ptr(),field%c_ptr(),maximum%c_ptr(),location%c_ptr()) 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%c_ptr(),field%c_ptr(),minimum%c_ptr()) 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%c_ptr(),field%c_ptr(),maximum%c_ptr()) 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%c_ptr(),field%c_ptr(),sum%c_ptr(),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%c_ptr(),field%c_ptr(),sum%c_ptr(),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%c_ptr(),field%c_ptr(),mean%c_ptr(),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%c_ptr(),field%c_ptr(),mean%c_ptr(),stddev%c_ptr(),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 ecmwf-atlas-b0f79ee/src/atlas_f/interpolation/0000775000175000017500000000000015263665111021701 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas_f/interpolation/atlas_Interpolation_module.F900000664000175000017500000001341415263665111027544 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr(), & source%c_ptr(),target%c_ptr())) 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%c_ptr(), & source%c_ptr(),target%c_ptr())) 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%c_ptr(), & source%c_ptr(),target%c_ptr())) 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%c_ptr(),source%c_ptr(),target%c_ptr()) 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%c_ptr(),source%c_ptr(),target%c_ptr()) 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%c_ptr(),source%c_ptr(),target%c_ptr()) 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%c_ptr(),source%c_ptr(),target%c_ptr()) 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 ecmwf-atlas-b0f79ee/src/atlas_f/domain/0000775000175000017500000000000015263665111020261 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas_f/domain/atlas_Domain_module.F900000664000175000017500000001327615263665111024512 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr()) ) 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%c_ptr(), 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%c_ptr(), 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%c_ptr()) ) 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%c_ptr()) 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%c_ptr()) 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%c_ptr()) 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%c_ptr()) 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 ecmwf-atlas-b0f79ee/src/atlas_f/redistribution/0000775000175000017500000000000015263665111022060 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas_f/redistribution/atlas_Redistribution_module.F900000664000175000017500000001037515263665111030105 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr(), fspace2%c_ptr(), config%c_ptr()) ) 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%c_ptr(), field_1%c_ptr(), field_2%c_ptr()) 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%c_ptr())) 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%c_ptr())) 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 ecmwf-atlas-b0f79ee/src/atlas_f/CMakeLists.txt0000664000175000017500000003253515263665111021562 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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.fypp 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_Deprecation_module.F90 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 ) ecmwf-atlas-b0f79ee/src/atlas_f/mesh/0000775000175000017500000000000015263665111017746 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas_f/mesh/atlas_mesh_Edges_module.F900000664000175000017500000000375115263665111025030 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas_f/mesh/atlas_ElementType_module.F900000664000175000017500000001032115263665111025207 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr()) 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%c_ptr()) 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%c_ptr()) 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%c_ptr()) 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 ecmwf-atlas-b0f79ee/src/atlas_f/mesh/atlas_MeshGenerator_module.F900000664000175000017500000001145315263665111025526 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr()) ) 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%c_ptr(),grid%c_ptr(),distribution%c_ptr()) ) else mesh = atlas_Mesh( atlas__MeshGenerator__generate__grid( & this%c_ptr(),grid%c_ptr()) ) 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%c_ptr(),grid%c_ptr(),partitioner%c_ptr()) ) 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 ecmwf-atlas-b0f79ee/src/atlas_f/mesh/atlas_Mesh_module.F900000664000175000017500000001670615263665111023665 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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 atlas_Grid_module, only: atlas_Grid use atlas_GridDistribution_module, only: atlas_GridDistribution use atlas_Partitioner_module, only: atlas_Partitioner use atlas_Config_module, only: atlas_Config 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 :: atlas_GridDistribution private :: atlas_Partitioner private :: atlas_Config 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 module procedure atlas_Mesh__ctor_grid module procedure atlas_Mesh__ctor_grid_distribution module procedure atlas_Mesh__ctor_grid_partitioner 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 atlas_Mesh__ctor_grid(grid, config) result(this) use atlas_mesh_c_binding type(atlas_Mesh) :: this class(atlas_Grid), intent(in) :: grid type(atlas_Config), intent(in), optional :: config type(atlas_Config) :: opt_config if (present(config)) then call this%reset_c_ptr( atlas__Mesh__new_grid(grid%c_ptr(), config%c_ptr()) ) else opt_config = atlas_Config() call this%reset_c_ptr( atlas__Mesh__new_grid(grid%c_ptr(), opt_config%c_ptr()) ) call opt_config%final() endif call this%return() end function atlas_Mesh__ctor_grid !------------------------------------------------------------------------------- function atlas_Mesh__ctor_grid_distribution(grid, distribution, config) result(this) use atlas_mesh_c_binding type(atlas_Mesh) :: this class(atlas_Grid), intent(in) :: grid class(atlas_GridDistribution), intent(in) :: distribution type(atlas_Config), intent(in), optional :: config type(atlas_Config) :: opt_config if (present(config)) then call this%reset_c_ptr( atlas__Mesh__new_grid_distribution( & grid%c_ptr(), distribution%c_ptr(), config%c_ptr()) ) else opt_config = atlas_Config() call this%reset_c_ptr( atlas__Mesh__new_grid_distribution( & grid%c_ptr(), distribution%c_ptr(), opt_config%c_ptr()) ) call opt_config%final() endif call this%return() end function atlas_Mesh__ctor_grid_distribution !------------------------------------------------------------------------------- function atlas_Mesh__ctor_grid_partitioner(grid, partitioner, config) result(this) use atlas_mesh_c_binding type(atlas_Mesh) :: this class(atlas_Grid), intent(in) :: grid class(atlas_Partitioner), intent(in) :: partitioner type(atlas_Config), intent(in), optional :: config type(atlas_Config) :: opt_config if (present(config)) then call this%reset_c_ptr( atlas__Mesh__new_grid_partitioner( & grid%c_ptr(), partitioner%c_ptr(), config%c_ptr()) ) else opt_config = atlas_Config() call this%reset_c_ptr( atlas__Mesh__new_grid_partitioner( & grid%c_ptr(), partitioner%c_ptr(), opt_config%c_ptr()) ) call opt_config%final() endif call this%return() end function atlas_Mesh__ctor_grid_partitioner !------------------------------------------------------------------------------- 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) end function !------------------------------------------------------------------------------- subroutine update_device(this) use atlas_mesh_c_binding class(atlas_Mesh), intent(inout) :: this call atlas__Mesh__update_device(this%c_ptr()) end subroutine !------------------------------------------------------------------------------- subroutine update_host(this) use atlas_mesh_c_binding class(atlas_Mesh), intent(inout) :: this call atlas__Mesh__update_host(this%c_ptr()) 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%c_ptr()) 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 ecmwf-atlas-b0f79ee/src/atlas_f/mesh/atlas_mesh_actions_module.F900000664000175000017500000001170215263665111025434 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr()) 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%c_ptr()) 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%c_ptr()) 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%c_ptr()) 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%c_ptr()) 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%c_ptr(),nelems) end subroutine atlas_build_halo subroutine atlas_build_edges(mesh, config) use atlas_BuildEdges_c_binding use atlas_Mesh_module, only: atlas_Mesh use atlas_Config_module, only: atlas_Config type(atlas_Mesh), intent(inout) :: mesh type(atlas_Config), intent(in), optional :: config if (present(config)) then call atlas__build_edges_config(mesh%c_ptr(), config%c_ptr()) else call atlas__build_edges(mesh%c_ptr()) end if 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%c_ptr()) 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%c_ptr()) 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%c_ptr()) 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%c_ptr()) 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%c_ptr()) 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%c_ptr(),c_str(filename)) end subroutine atlas_write_load_balance_report ! ----------------------------------------------------------------------------- end module atlas_mesh_actions_module ecmwf-atlas-b0f79ee/src/atlas_f/mesh/atlas_MeshBuilder_module.F900000664000175000017500000001164615263665111025172 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%c_ptr(), & & 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 ecmwf-atlas-b0f79ee/src/atlas_f/mesh/atlas_HybridElements_module.F900000664000175000017500000003116715263665111025705 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr()) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr(),elementtype%c_ptr(),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%c_ptr(),elementtype%c_ptr(),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%c_ptr(),& & elementtype%c_ptr(),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%c_ptr(),& & elementtype%c_ptr(),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%c_ptr(),& & elementtype%c_ptr(),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%c_ptr(),& & elementtype%c_ptr(),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%c_ptr(), field%c_ptr()) 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%c_ptr()) 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%c_ptr()) 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%c_ptr(),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%c_ptr(),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%c_ptr(),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%c_ptr(),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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr(),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%c_ptr(),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 ecmwf-atlas-b0f79ee/src/atlas_f/mesh/atlas_Elements_module.F900000664000175000017500000002035415263665111024537 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr()) 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%c_ptr()) ) !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%c_ptr()) ) !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%c_ptr()) ) !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%c_ptr()) ) 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%c_ptr(),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%c_ptr(),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%c_ptr()) 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%c_ptr(),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%c_ptr(),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%c_ptr(),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%c_ptr(),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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) + 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%c_ptr()) 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 ecmwf-atlas-b0f79ee/src/atlas_f/mesh/atlas_mesh_Cells_module.F900000664000175000017500000000377215263665111025046 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas_f/mesh/atlas_mesh_Nodes_module.F900000664000175000017500000002303315263665111025044 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr()) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr(),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%c_ptr(), connectivity%c_ptr()) 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%c_ptr(), field%c_ptr()) 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%c_ptr(),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%c_ptr()) 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%c_ptr(),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%c_ptr(),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%c_ptr(),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%c_ptr(),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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr(),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%c_ptr(),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%c_ptr(),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 ecmwf-atlas-b0f79ee/src/atlas_f/mesh/atlas_Connectivity_module.F900000664000175000017500000006036315263665111025445 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr(),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%c_ptr()) 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%c_ptr()) 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%c_ptr(),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%c_ptr(),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%c_ptr(),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%c_ptr(),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%c_ptr(),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%c_ptr(),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%c_ptr()) 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%c_ptr(),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%c_ptr(),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%c_ptr()) 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%c_ptr()) 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%c_ptr()) + 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%c_ptr(),ctxt) if( have_ctxt == 1 ) then call c_f_pointer(ctxt,this%access) else allocate( this%access ) call atlas__connectivity__register_ctxt ( this%c_ptr(), c_loc(this%access) ) call atlas__connectivity__register_update( this%c_ptr(), c_funloc(update_access_c) ) call atlas__connectivity__register_delete( this%c_ptr(), c_funloc(delete_access_c) ) this%access%connectivity_ptr = this%c_ptr() 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 ecmwf-atlas-b0f79ee/src/atlas_f/projection/0000775000175000017500000000000015263665111021166 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas_f/projection/atlas_Projection_module.F900000664000175000017500000001640315263665111026317 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr()) ) 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%c_ptr(), 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%c_ptr(), 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%c_ptr()) ) 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%c_ptr(), 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%c_ptr(), 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 ecmwf-atlas-b0f79ee/src/atlas_f/atlas_f.h.in0000664000175000017500000000220415263665111021177 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 #define ATLAS_FCKIT_VERSION_AT_LEAST(x, y, z) (ATLAS_FCKIT_VERSION_INT >= x * 10000 + y * 100 + z) #endif ecmwf-atlas-b0f79ee/src/atlas_f/field/0000775000175000017500000000000015263665111020075 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas_f/field/atlas_MultiField_module.F900000664000175000017500000001241015263665111025142 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr()) ) 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%c_ptr()) 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%c_ptr()) 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 ecmwf-atlas-b0f79ee/src/atlas_f/field/atlas_FieldSet_module.fypp0000664000175000017500000007142515263665111025236 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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, private :: shape_array_by_name procedure, private :: shape_idx_by_name procedure, private :: shape_array_by_idx procedure, private :: shape_idx_by_idx generic, public :: shape => shape_array_by_name, shape_idx_by_name, shape_array_by_idx, shape_idx_by_idx 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%c_ptr(), 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%c_ptr(), 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_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__device_data_${ctype}$_specf(this%c_ptr(), 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_device_data_${dtype}$_r${rank}$_by_idx(this, idx, field) 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__device_data_${ctype}$_specf_by_idx(this%c_ptr(), idx-1, 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%c_ptr()) 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%c_ptr(), field%c_ptr()) 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%c_ptr(), fset%c_ptr()) 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%c_ptr(), 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%c_ptr()) 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), int(idx-1_c_int,ATLAS_KIND_IDX) ) ) ! C index call field%return() end function !------------------------------------------------------------------------------- function shape_array_by_name(this, name) result(shape) use atlas_fieldset_c_binding use fckit_c_interop_module, only: c_str use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_f_pointer class(atlas_FieldSet), intent(in) :: this character(len=*), intent(in) :: name integer, allocatable :: shape(:) type(c_ptr) :: shape_c_ptr integer, pointer :: shape_f_ptr(:) integer(c_int) :: field_rank call atlas__FieldSet__shapef(this%c_ptr(), c_str(name), 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 !------------------------------------------------------------------------------- function shape_idx_by_name(this, name, dim_idx) result(shape_val) use atlas_fieldset_c_binding use fckit_c_interop_module, only: c_str use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_f_pointer @:ENABLE_ATLAS_MACROS() class(atlas_FieldSet), intent(in) :: this character(len=*), intent(in) :: name integer, intent(in) :: dim_idx integer :: shape_val type(c_ptr) :: shape_c_ptr integer, pointer :: shape_f_ptr(:) integer(c_int) :: field_rank call atlas__FieldSet__shapef(this%c_ptr(), c_str(name), shape_c_ptr, field_rank) call c_f_pointer ( shape_c_ptr , shape_f_ptr , (/field_rank/) ) @:ATLAS_ASSERT( dim_idx <= field_rank ) shape_val = shape_f_ptr(dim_idx) end function !------------------------------------------------------------------------------- function shape_array_by_idx(this, idx) result(shape) use atlas_fieldset_c_binding use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_f_pointer class(atlas_FieldSet), intent(in) :: this integer, intent(in) :: idx integer, allocatable :: shape(:) type(c_ptr) :: shape_c_ptr integer, pointer :: shape_f_ptr(:) integer(c_int) :: field_rank call atlas__FieldSet__shapef_by_idx(this%c_ptr(), idx-1, 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 !------------------------------------------------------------------------------- function shape_idx_by_idx(this, idx, dim_idx) result(shape_val) use atlas_fieldset_c_binding use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_f_pointer @:ENABLE_ATLAS_MACROS() class(atlas_FieldSet), intent(in) :: this integer, intent(in) :: idx, dim_idx integer :: shape_val type(c_ptr) :: shape_c_ptr integer, pointer :: shape_f_ptr(:) integer(c_int) :: field_rank call atlas__FieldSet__shapef_by_idx(this%c_ptr(), idx-1, shape_c_ptr, field_rank) call c_f_pointer ( shape_c_ptr , shape_f_ptr , (/field_rank/) ) @:ATLAS_ASSERT( dim_idx <= field_rank ) shape_val = shape_f_ptr(dim_idx) 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%c_ptr(), on_device_int) end subroutine !------------------------------------------------------------------------------- subroutine set_host_needs_update_value(this, value) use, intrinsic :: iso_c_binding, only : c_int use atlas_fieldset_c_binding class(atlas_FieldSet), intent(inout) :: this logical, intent(in), optional :: value integer(c_int) :: value_int value_int = 1 if (present(value)) then if (.not. value) then value_int = 0 end if end if call atlas__FieldSet__set_host_needs_update(this%c_ptr(), value_int) end subroutine !------------------------------------------------------------------------------- subroutine set_host_needs_update_idx(this, field_indices, value) use, intrinsic :: iso_c_binding, only : c_int use atlas_fieldset_c_binding class(atlas_FieldSet), intent(inout) :: this integer, intent(in) :: field_indices(:) logical, intent(in), optional :: value 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) call atlas__FieldSet__set_host_needs_update_by_idx(this%c_ptr(), field_indices(i)-1, 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_fieldset_c_binding use fckit_c_interop_module, only: c_str class(atlas_FieldSet), intent(inout) :: this character(*), intent(in) :: field_names(:) logical, intent(in), optional :: value 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) call atlas__FieldSet__set_host_needs_update_name(this%c_ptr(), c_str(field_names(i)), value_int) end do end subroutine !------------------------------------------------------------------------------- subroutine set_device_needs_update_value(this, value) use, intrinsic :: iso_c_binding, only : c_int use atlas_fieldset_c_binding class(atlas_FieldSet), intent(inout) :: this logical, intent(in), optional :: value integer(c_int) :: value_int value_int = 1 if (present(value)) then if (.not. value) then value_int = 0 end if end if call atlas__FieldSet__set_device_needs_update(this%c_ptr(), value_int) end subroutine !------------------------------------------------------------------------------- subroutine set_device_needs_update_idx(this, field_indices, value) use, intrinsic :: iso_c_binding, only : c_int use atlas_fieldset_c_binding class(atlas_FieldSet), intent(inout) :: this integer, intent(in) :: field_indices(:) logical, intent(in), optional :: value 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) call atlas__FieldSet__set_device_needs_update_by_idx(this%c_ptr(), field_indices(i)-1, 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_fieldset_c_binding use fckit_c_interop_module, only: c_str class(atlas_FieldSet), intent(inout) :: this character(*), intent(in) :: field_names(:) logical, intent(in), optional :: value 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) call atlas__FieldSet__set_device_needs_update_name(this%c_ptr(), c_str(field_names(i)), value_int) end do end subroutine !------------------------------------------------------------------------------- subroutine sync_host_device_idx(this, field_indices) use, intrinsic :: iso_c_binding, only : c_int use atlas_fieldset_c_binding class(atlas_FieldSet), intent(inout) :: this integer, intent(in), optional :: field_indices(:) integer(c_int) :: i if (present(field_indices)) then do i = 1, size(field_indices) call atlas__FieldSet__sync_host_device_by_idx(this%c_ptr(), field_indices(i)-1) end do else call atlas__FieldSet__sync_host_device(this%c_ptr()) end if end subroutine !------------------------------------------------------------------------------- subroutine sync_host_device_name(this, field_names) use, intrinsic :: iso_c_binding, only : c_int use atlas_fieldset_c_binding use fckit_c_interop_module, only: c_str class(atlas_FieldSet), intent(inout) :: this character(*), intent(in) :: field_names(:) integer(c_int) :: i do i = 1, size(field_names) call atlas__FieldSet__sync_host_device_name(this%c_ptr(), c_str(field_names(i))) end do end subroutine !------------------------------------------------------------------------------- subroutine sync_host_idx(this, field_indices) use, intrinsic :: iso_c_binding, only : c_int use atlas_fieldset_c_binding class(atlas_FieldSet), intent(inout) :: this integer, intent(in), optional :: field_indices(:) integer(c_int) :: i if (present(field_indices)) then do i = 1, size(field_indices) call atlas__FieldSet__sync_host_by_idx(this%c_ptr(), field_indices(i)-1) end do else call atlas__FieldSet__sync_host(this%c_ptr()) end if end subroutine !------------------------------------------------------------------------------- subroutine sync_host_name(this, field_names) use, intrinsic :: iso_c_binding, only : c_int use atlas_fieldset_c_binding use fckit_c_interop_module, only: c_str class(atlas_FieldSet), intent(inout) :: this character(*), intent(in) :: field_names(:) integer(c_int) :: i do i = 1, size(field_names) call atlas__FieldSet__sync_host_name(this%c_ptr(), c_str(field_names(i))) end do end subroutine !------------------------------------------------------------------------------- subroutine sync_device_idx(this, field_indices) use, intrinsic :: iso_c_binding, only : c_int use atlas_fieldset_c_binding class(atlas_FieldSet), intent(inout) :: this integer, intent(in), optional :: field_indices(:) integer(c_int) :: i if (present(field_indices)) then do i = 1, size(field_indices) call atlas__FieldSet__sync_device_by_idx(this%c_ptr(), field_indices(i)-1) end do else call atlas__FieldSet__sync_device(this%c_ptr()) end if end subroutine !------------------------------------------------------------------------------- subroutine sync_device_name(this, field_names) use, intrinsic :: iso_c_binding, only : c_int use atlas_fieldset_c_binding use fckit_c_interop_module, only: c_str class(atlas_FieldSet), intent(inout) :: this character(*), intent(in) :: field_names(:) integer(c_int) :: i do i = 1, size(field_names) call atlas__FieldSet__sync_device_name(this%c_ptr(), c_str(field_names(i))) end do end subroutine !------------------------------------------------------------------------------- subroutine allocate_device_idx(this, field_indices) use, intrinsic :: iso_c_binding, only : c_int use atlas_fieldset_c_binding class(atlas_FieldSet), intent(inout) :: this integer, intent(in), optional :: field_indices(:) integer(c_int) :: i if (present(field_indices)) then do i = 1, size(field_indices) call atlas__FieldSet__allocate_device_by_idx(this%c_ptr(), field_indices(i)-1) end do else call atlas__FieldSet__allocate_device(this%c_ptr()) end if end subroutine !------------------------------------------------------------------------------- subroutine allocate_device_name(this, field_names) use, intrinsic :: iso_c_binding, only : c_int use atlas_fieldset_c_binding use fckit_c_interop_module, only: c_str class(atlas_FieldSet), intent(inout) :: this character(*), intent(in) :: field_names(:) integer(c_int) :: i do i = 1, size(field_names) call atlas__FieldSet__allocate_device_name(this%c_ptr(), c_str(field_names(i))) end do end subroutine !------------------------------------------------------------------------------- subroutine update_device_idx(this, field_indices) use, intrinsic :: iso_c_binding, only : c_int use atlas_fieldset_c_binding class(atlas_FieldSet), intent(inout) :: this integer, intent(in), optional :: field_indices(:) integer(c_int) :: i if (present(field_indices)) then do i = 1, size(field_indices) call atlas__FieldSet__update_device_by_idx(this%c_ptr(), field_indices(i)-1) end do else call atlas__FieldSet__update_device(this%c_ptr()) end if end subroutine !------------------------------------------------------------------------------- subroutine update_device_name(this, field_names) use, intrinsic :: iso_c_binding, only : c_int use atlas_fieldset_c_binding use fckit_c_interop_module, only: c_str class(atlas_FieldSet), intent(inout) :: this character(*), intent(in) :: field_names(:) integer(c_int) :: i do i = 1, size(field_names) call atlas__FieldSet__update_device_name(this%c_ptr(), c_str(field_names(i))) end do end subroutine !------------------------------------------------------------------------------- subroutine update_host_idx(this, field_indices) use, intrinsic :: iso_c_binding, only : c_int use atlas_fieldset_c_binding class(atlas_FieldSet), intent(inout) :: this integer, intent(in), optional :: field_indices(:) integer(c_int) :: i if (present(field_indices)) then do i = 1, size(field_indices) call atlas__FieldSet__update_host_by_idx(this%c_ptr(), field_indices(i)-1) end do else call atlas__FieldSet__update_host(this%c_ptr()) end if end subroutine !------------------------------------------------------------------------------- subroutine update_host_name(this, field_names) use, intrinsic :: iso_c_binding, only : c_int use atlas_fieldset_c_binding use fckit_c_interop_module, only: c_str class(atlas_FieldSet), intent(inout) :: this character(*), intent(in) :: field_names(:) integer(c_int) :: i do i = 1, size(field_names) call atlas__FieldSet__update_host_name(this%c_ptr(), c_str(field_names(i))) end do end subroutine !------------------------------------------------------------------------------- subroutine deallocate_device_idx(this, field_indices) use, intrinsic :: iso_c_binding, only : c_int use atlas_fieldset_c_binding class(atlas_FieldSet), intent(inout) :: this integer, optional :: field_indices(:) integer(c_int) :: i if (present(field_indices)) then do i = 1, size(field_indices) call atlas__FieldSet__deallocate_device_by_idx(this%c_ptr(), field_indices(i)-1) end do else call atlas__FieldSet__deallocate_device(this%c_ptr()) end if end subroutine !------------------------------------------------------------------------------- subroutine deallocate_device_name(this, field_names) use, intrinsic :: iso_c_binding, only : c_int use atlas_fieldset_c_binding use fckit_c_interop_module, only: c_str class(atlas_FieldSet), intent(inout) :: this character(*), intent(in) :: field_names(:) integer(c_int) :: i do i = 1, size(field_names) call atlas__FieldSet__deallocate_device_name(this%c_ptr(), c_str(field_names(i))) 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%c_ptr(), 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 ecmwf-atlas-b0f79ee/src/atlas_f/field/atlas_Field_module.fypp0000664000175000017500000010104115263665111024546 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) 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%c_ptr()) ) 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%c_ptr(),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%c_ptr()) 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%c_ptr()) 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%c_ptr()) 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%c_ptr()) 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%c_ptr()) 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%c_ptr()) ) 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(),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%c_ptr(),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%c_ptr(),functionspace%c_ptr()) 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%c_ptr(),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%c_ptr(),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%c_ptr()) == 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%c_ptr()) == 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%c_ptr()) end subroutine !------------------------------------------------------------------------------- subroutine update_host(this) use atlas_field_c_binding class(atlas_Field), intent(inout) :: this call atlas__Field__update_host(this%c_ptr()) 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%c_ptr()) end subroutine !------------------------------------------------------------------------------- subroutine sync_host(this) use atlas_field_c_binding class(atlas_Field), intent(inout) :: this call atlas__Field__sync_host(this%c_ptr()) end subroutine !------------------------------------------------------------------------------- subroutine sync_device(this) use atlas_field_c_binding class(atlas_Field), intent(inout) :: this call atlas__Field__sync_device(this%c_ptr()) end subroutine !------------------------------------------------------------------------------- subroutine allocate_device(this) use atlas_field_c_binding class(atlas_Field), intent(inout) :: this call atlas__Field__allocate_device(this%c_ptr()) end subroutine !------------------------------------------------------------------------------- subroutine deallocate_device(this) use atlas_field_c_binding class(atlas_Field), intent(inout) :: this call atlas__Field__deallocate_device(this%c_ptr()) 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%c_ptr()) == 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%c_ptr(), 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%c_ptr(), 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%c_ptr()) 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%c_ptr()) == 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 ecmwf-atlas-b0f79ee/src/atlas_f/field/atlas_State_module.F900000664000175000017500000001367015263665111024175 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr(),c_str(generator),params%c_ptr()) else p = atlas_Config() call atlas__State__initialize(this%c_ptr(),c_str(generator),p%c_ptr()) 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%c_ptr(),field%c_ptr()) 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%c_ptr(),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%c_ptr(),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%c_ptr()) 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%c_ptr(),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%c_ptr(),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%c_ptr()) ) 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 ecmwf-atlas-b0f79ee/src/atlas_f/runtime/0000775000175000017500000000000015263665111020475 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas_f/runtime/atlas_Trace_module.F900000664000175000017500000001577615263665111024564 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr() ) == 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%c_ptr() ) end function !------------------------------------------------------------------------------- subroutine start( this ) use atlas_Trace_c_binding class(atlas_Trace) :: this call atlas_Trace__start( this%c_ptr() ) end subroutine !------------------------------------------------------------------------------- subroutine stop( this ) use atlas_Trace_c_binding class(atlas_Trace) :: this call atlas_Trace__stop( this%c_ptr() ) end subroutine !------------------------------------------------------------------------------- subroutine pause( this ) use atlas_Trace_c_binding class(atlas_Trace) :: this call atlas_Trace__pause( this%c_ptr() ) end subroutine !------------------------------------------------------------------------------- subroutine resume( this ) use atlas_Trace_c_binding class(atlas_Trace) :: this call atlas_Trace__resume( this%c_ptr() ) 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 ecmwf-atlas-b0f79ee/src/atlas_f/runtime/atlas_trace.h0000664000175000017500000000333715263665111023136 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the 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 ecmwf-atlas-b0f79ee/src/atlas_f/runtime/atlas_Deprecation_module.F900000664000175000017500000000353415263665111025750 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. module atlas_Deprecation_module implicit none public :: atlas_deprecation_errors public :: atlas_deprecation_warnings private contains logical function atlas_deprecation_warnings() result(enabled) logical, save :: initialized = .false. logical, save :: value = .true. character(len=16) :: env_value integer :: env_status integer :: env_length integer :: env_int integer :: read_status if (.not. initialized) then call get_environment_variable("ATLAS_DEPRECATION_WARNINGS", env_value, env_length, env_status) if (env_status == 0 .and. env_length > 0) then read(env_value(1:env_length), *, iostat=read_status) env_int if (read_status == 0) then value = (env_int /= 0) end if end if initialized = .true. end if enabled = value end function logical function atlas_deprecation_errors() result(enabled) logical, save :: initialized = .false. logical, save :: value = .false. character(len=16) :: env_value integer :: env_status integer :: env_length integer :: env_int integer :: read_status if (.not. initialized) then call get_environment_variable("ATLAS_DEPRECATION_ERRORS", env_value, env_length, env_status) if (env_status == 0 .and. env_length > 0) then read(env_value(1:env_length), *, iostat=read_status) env_int if (read_status == 0) then value = (env_int /= 0) end if end if initialized = .true. end if enabled = value end function end module atlas_Deprecation_moduleecmwf-atlas-b0f79ee/src/atlas_f/runtime/atlas_trace.cc0000664000175000017500000000720515263665111023272 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas_f/numerics/0000775000175000017500000000000015263665111020637 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas_f/numerics/atlas_Method_module.F900000664000175000017500000000464615263665111025102 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr()) 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 ecmwf-atlas-b0f79ee/src/atlas_f/numerics/atlas_Nabla_module.F900000664000175000017500000001105215263665111024664 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr(),config%c_ptr()) ) else opt_config = atlas_Config() call this%reset_c_ptr( atlas__Nabla__create(method%c_ptr(),opt_config%c_ptr()) ) 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%c_ptr(),scalar%c_ptr(),grad%c_ptr()) 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%c_ptr(),vector%c_ptr(),div%c_ptr()) 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%c_ptr(),vector%c_ptr(),curl%c_ptr()) 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%c_ptr(),scalar%c_ptr(),lapl%c_ptr()) 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%c_ptr()) ) 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 ecmwf-atlas-b0f79ee/src/atlas_f/numerics/atlas_fvm_module.F900000664000175000017500000000723215263665111024444 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr(), & config%c_ptr()) ) else opt_config = atlas_Config() call this%reset_c_ptr( atlas__numerics__fvm__Method__new(mesh%c_ptr(), & opt_config%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr()) ) 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 ecmwf-atlas-b0f79ee/src/atlas_f/atlas_module.F900000664000175000017500000002235315263665111021750 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas_f/trans/0000775000175000017500000000000015263665111020141 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas_f/trans/atlas_Trans_module.F900000664000175000017500000002644215263665111024251 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr(), nsmax, config%c_ptr() ) ) else call this%reset_c_ptr( atlas__Trans__new_config( grid%c_ptr(), 0, config%c_ptr() ) ) endif else if( present(nsmax) ) then call this%reset_c_ptr( atlas__Trans__new( grid%c_ptr(), nsmax ) ) else call this%reset_c_ptr( atlas__Trans__new( grid%c_ptr(), 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%c_ptr()) end function function truncation( this ) use atlas_trans_c_binding integer :: truncation class(atlas_Trans) :: this truncation = atlas__Trans__truncation (this%c_ptr()) 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%c_ptr()) ) 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%c_ptr()) ) 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%c_ptr() ) else p = atlas_Config() endif call atlas__Trans__dirtrans_fieldset( this%c_ptr(), & & gpfields%c_ptr(), & & spfields%c_ptr(), & & p%c_ptr() ) 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%c_ptr() ) else p = atlas_Config() endif call atlas__Trans__invtrans_fieldset( this%c_ptr(), & & spfields%c_ptr(), & & gpfields%c_ptr(), & & p%c_ptr() ) 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%c_ptr() ) else p = atlas_Config() endif call atlas__Trans__dirtrans_field( this%c_ptr(), & & gpfield%c_ptr(), & & spfield%c_ptr(), & & p%c_ptr() ) 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%c_ptr() ) else p = atlas_Config() endif call atlas__Trans__dirtrans_wind2vordiv_field( this%c_ptr(), & & gpwind%c_ptr(), & & spvor%c_ptr(), & & spdiv%c_ptr(), & & p%c_ptr() ) 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%c_ptr() ) else p = atlas_Config() endif call atlas__Trans__invtrans_field( this%c_ptr(), & & spfield%c_ptr(), & & gpfield%c_ptr(), & & p%c_ptr() ) 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%c_ptr() ) else p = atlas_Config() endif call atlas__Trans__invtrans_vordiv2wind_field( this%c_ptr(), & & spvor%c_ptr(), & & spdiv%c_ptr(), & & gpwind%c_ptr(), & & p%c_ptr() ) 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%c_ptr(), & & spfield%c_ptr(), & & gpfield%c_ptr(), & & config%c_ptr()) 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 ecmwf-atlas-b0f79ee/src/atlas_f/atlas_f.fypp0000664000175000017500000000461015263665111021324 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas_f/util/0000775000175000017500000000000015263665111017767 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas_f/util/atlas_KDTree_module.F900000664000175000017500000004331515263665111024124 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr() ) ) 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%c_ptr()) 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%c_ptr()) == 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%c_ptr()) 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr()) 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr() ) ) 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 ecmwf-atlas-b0f79ee/src/atlas_f/util/atlas_kinds_module.F900000664000175000017500000000332215263665111024110 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas_f/util/atlas_functions_module.F900000664000175000017500000000522315263665111025012 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas_f/util/atlas_Geometry_module.F900000664000175000017500000002017015263665111024573 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr()) 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr()) 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%c_ptr()) 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 ecmwf-atlas-b0f79ee/src/atlas_f/util/atlas_Metadata_module.F900000664000175000017500000003376315263665111024534 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr()) 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(),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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(),channel%c_ptr()) 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%c_ptr(),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 ecmwf-atlas-b0f79ee/src/atlas_f/util/atlas_Config_module.F900000664000175000017500000005074515263665111024220 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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%c_ptr(), 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%c_ptr(), c_str(name), value%c_ptr() ) 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)%c_ptr() enddo call atlas__Config__set_config_list(this%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), c_str(name), value%c_ptr() ) 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%c_ptr(), 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%c_ptr(),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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(),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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(), 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%c_ptr(),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 ecmwf-atlas-b0f79ee/src/atlas_f/util/atlas_JSON_module.F900000664000175000017500000000612215263665111023552 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas_f/util/atlas_allocate_module.F900000664000175000017500000002233015263665111024564 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/CMakeLists.txt0000664000175000017500000000616015263665111020144 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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() ecmwf-atlas-b0f79ee/src/atlas_acc_support/0000775000175000017500000000000015263665111021107 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas_acc_support/atlas_acc.cc0000664000175000017500000001107215263665111023331 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 } } ecmwf-atlas-b0f79ee/src/atlas_acc_support/CMakeLists.txt0000664000175000017500000000160215263665111023646 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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() ecmwf-atlas-b0f79ee/src/atlas_acc_support/atlas_acc.h0000664000175000017500000000211015263665111023164 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas_acc_support/atlas_acc.F900000664000175000017500000000504315263665111023303 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 ecmwf-atlas-b0f79ee/src/atlas/0000775000175000017500000000000015263665111016505 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/linalg/0000775000175000017500000000000015263665111017753 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/linalg/View.h0000664000175000017500000000444115263665111021041 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/Indexing.h0000664000175000017500000000104715263665111021673 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/dense.h0000664000175000017500000000104415263665111021221 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/Introspection.h0000664000175000017500000001512015263665111022763 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/sparse.h0000664000175000017500000000132415263665111021421 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/dense/0000775000175000017500000000000015263665111021051 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/linalg/dense/Backend.cc0000664000175000017500000001005215263665111022705 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/dense/MatrixMultiply_EckitLinalg.h0000664000175000017500000000214215263665111026473 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/dense/Backend.h0000664000175000017500000000230515263665111022551 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/dense/MatrixMultiply.h0000664000175000017500000000364515263665111024236 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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" ecmwf-atlas-b0f79ee/src/atlas/linalg/dense/MatrixMultiply.tcc0000664000175000017500000000446315263665111024557 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/dense/MatrixMultiply_EckitLinalg.cc0000664000175000017500000000441315263665111026634 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/fft.h0000664000175000017500000000074415263665111020710 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/fft/0000775000175000017500000000000015263665111020532 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/linalg/fft/FFTW.cc0000664000175000017500000001312215263665111021606 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/fft/FFT.h0000664000175000017500000001223515263665111021325 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/fft/pocketfft.cc0000664000175000017500000000712615263665111023034 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 #define POCKETFFT_NAMESPACE atlas::detail::pocketfft #include "pocketfft_hdronly.h" ATLAS_SUPPRESS_WARNINGS_PUSH namespace atlas::linalg { namespace pf = POCKETFFT_NAMESPACE; 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 pf::stride_t stride_in{sizeof(std::complex)}; // in bytes static const pf::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 = pf::BACKWARD; pf::shape_t shape_out{size_out}; pf::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 pf::stride_t stride_in{sizeof(std::complex)}; // in bytes static const pf::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 = pf::BACKWARD; const pf::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 ecmwf-atlas-b0f79ee/src/atlas/linalg/sparse/0000775000175000017500000000000015263665111021250 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/linalg/sparse/SparseMatrixStorage.cc0000664000175000017500000001546115263665111025535 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; } } } }ecmwf-atlas-b0f79ee/src/atlas/linalg/sparse/SparseMatrixMultiply_HicSparse.h0000664000175000017500000000511715263665111027550 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 atlasecmwf-atlas-b0f79ee/src/atlas/linalg/sparse/SparseMatrixMultiply_HicSparse.cc0000664000175000017500000002642315263665111027711 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 atlasecmwf-atlas-b0f79ee/src/atlas/linalg/sparse/Backend.cc0000664000175000017500000000640215263665111023110 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/sparse/SparseMatrixStorage.h0000664000175000017500000002552115263665111025375 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/sparse/MakeSparseMatrixStorageEigen.h0000664000175000017500000001473215263665111027145 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 atlasecmwf-atlas-b0f79ee/src/atlas/linalg/sparse/SparseMatrixMultiply.h0000664000175000017500000001227015263665111025605 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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" ecmwf-atlas-b0f79ee/src/atlas/linalg/sparse/MakeSparseMatrixStorageEckit.h0000664000175000017500000001334015263665111027147 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 atlasecmwf-atlas-b0f79ee/src/atlas/linalg/sparse/SparseMatrixMultiply_EckitLinalg.h0000664000175000017500000000365215263665111030057 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/sparse/Backend.h0000664000175000017500000000271115263665111022751 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/sparse/SparseMatrixView.h0000664000175000017500000000405115263665111024676 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/sparse/SparseMatrixMultiply_OpenMP.h0000664000175000017500000000733615263665111027032 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/sparse/SparseMatrixMultiply.tcc0000664000175000017500000002726415263665111026140 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/sparse/SparseMatrixMultiply_OpenMP.cc0000664000175000017500000003065715263665111027172 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/sparse/SparseMatrixTriplet.h0000664000175000017500000001464015263665111025414 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/sparse/SparseMatrixToTriplets.h0000664000175000017500000001277415263665111026110 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/sparse/SparseMatrixMultiply_EckitLinalg.cc0000664000175000017500000001242215263665111030210 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 ecmwf-atlas-b0f79ee/src/atlas/linalg/sparse/MakeEckitSparseMatrix.h0000664000175000017500000000736115263665111025630 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 ecmwf-atlas-b0f79ee/src/atlas/option/0000775000175000017500000000000015263665111020015 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/option/TransOptions.h0000664000175000017500000000563315263665111022640 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/option/TransOptions.cc0000664000175000017500000000361215263665111022771 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/option/Options.h0000664000175000017500000000633615263665111021631 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/option/Options.cc0000664000175000017500000000416715263665111021767 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/domain.h0000664000175000017500000000072515263665111020131 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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" ecmwf-atlas-b0f79ee/src/atlas/output/0000775000175000017500000000000015263665111020045 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/output/detail/0000775000175000017500000000000015263665111021307 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/output/detail/GmshImpl.cc0000664000175000017500000002226715263665111023347 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/parallel/mpi/mpi.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 = "auto"; config_.levels.clear(); config_.file = "output.msh"; config_.info = false; config_.element_partition_as_entity = false; config_.openmode = "w"; config_.coordinates = "xy"; config_.missing_value_policy = "skip"; config_.missing_value_fill = 0.; config_.masked_value_policy = "skip"; config_.masked_value_fill = 0.; 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); if (present.elements != "auto" && present.elements != "cells" && present.elements != "edges") { ATLAS_THROW_EXCEPTION("Invalid Gmsh elements='" << present.elements << "': expected 'auto', 'cells' or 'edges'"); } update.get("levels", present.levels); update.get("file", present.file); update.get("info", present.info); update.get("element_partition_as_entity", present.element_partition_as_entity); update.get("openmode", present.openmode); update.get("coordinates", present.coordinates); update.get("missing_value.policy", present.missing_value_policy); update.get("missing_value.fill", present.missing_value_fill); update.get("masked_value.policy", present.masked_value_policy); update.get("masked_value.fill", present.masked_value_fill); 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 == "cells"); gmsh.options.set("edges", c.elements == "edges"); gmsh.options.set("levels", c.levels); gmsh.options.set("info", c.info); gmsh.options.set("element_partition_as_entity", c.element_partition_as_entity); gmsh.options.set("nodes", c.coordinates); gmsh.options.set("missing_value.policy", c.missing_value_policy); gmsh.options.set("missing_value.fill", c.missing_value_fill); gmsh.options.set("masked_value.policy", c.masked_value_policy); gmsh.options.set("masked_value.fill", c.masked_value_fill); 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.elements == "auto") { const auto& comm = mpi::comm(mesh.mpi_comm()); idx_t nb_cells = mesh.cells().size(); comm.allReduceInPlace(nb_cells, eckit::mpi::Operation::SUM); if (nb_cells > 0) { c.elements = "cells"; } else { idx_t nb_edges = mesh.edges().size(); comm.allReduceInPlace(nb_edges, eckit::mpi::Operation::SUM); c.elements = nb_edges > 0 ? "edges" : "none"; } } 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 ecmwf-atlas-b0f79ee/src/atlas/output/detail/PointCloudIO.cc0000664000175000017500000003347215263665111024137 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/output/detail/PointCloudIO.h0000664000175000017500000001116215263665111023771 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/output/detail/GmshInterface.cc0000664000175000017500000000242315263665111024336 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/output/detail/GmshIO.h0000664000175000017500000001631515263665111022614 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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/EdgeColumns.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 Edges functionspace /// Depending on argument "mode", the fields will be appended, /// or existing file will be overwritten void write_delegate(const FieldSet& fieldset, const functionspace::EdgeColumns&, 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 Edges functionspace /// Depending on argument "mode", the fields will be appended, /// or existing file will be overwritten void write_delegate(const Field& field, const functionspace::EdgeColumns&, 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 ecmwf-atlas-b0f79ee/src/atlas/output/detail/GmshInterface.h0000664000175000017500000000204715263665111024202 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the 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 ecmwf-atlas-b0f79ee/src/atlas/output/detail/GmshImpl.h0000664000175000017500000000532615263665111023206 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 gather; bool ghost; bool info; bool element_partition_as_entity; bool configured_land_water; bool land; bool water; std::vector levels; std::string elements; std::string nodes; std::string file; std::string openmode; std::string coordinates; std::string missing_value_policy; double missing_value_fill; std::string masked_value_policy; double masked_value_fill; }; static void setGmshConfiguration(detail::GmshIO&, const Configuration&); private: mutable Configuration config_; void defaults(); }; // ----------------------------------------------------------------------------- } // namespace detail } // namespace output } // namespace atlas ecmwf-atlas-b0f79ee/src/atlas/output/detail/GmshIO.cc0000664000175000017500000024125615263665111022756 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 "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/EdgeColumns.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) + ".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) + ".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 << "4.1 0 " << sizeof(size_t) << "\n"; out << "$EndMeshFormat\n"; } // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- void write_header_binary(std::ostream& out) { out << "$MeshFormat\n"; out << "4.1 1 " << sizeof(size_t) << "\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()); } } } enum class MissingValuePolicy { SKIP, FILL, NOT_A_NUMBER, PRESERVE }; using MaskedValuePolicy = MissingValuePolicy; MissingValuePolicy missing_value_policy(const Metadata& gmsh_options) { const std::string policy = gmsh_options.get("missing_value.policy"); if (policy == "skip") { return MissingValuePolicy::SKIP; } if (policy == "fill") { return MissingValuePolicy::FILL; } if (policy == "nan") { return MissingValuePolicy::NOT_A_NUMBER; } if (policy == "preserve") { return MissingValuePolicy::PRESERVE; } ATLAS_THROW_EXCEPTION("Unsupported Gmsh missing_value.policy: " << policy); } MaskedValuePolicy masked_value_policy(const Metadata& gmsh_options) { const std::string policy = gmsh_options.get("masked_value.policy"); if (policy == "skip") { return MaskedValuePolicy::SKIP; } if (policy == "fill") { return MaskedValuePolicy::FILL; } if (policy == "nan") { return MaskedValuePolicy::NOT_A_NUMBER; } if (policy == "preserve") { return MaskedValuePolicy::PRESERVE; } ATLAS_THROW_EXCEPTION("Unsupported Gmsh masked_value.policy: " << policy); } template Value missing_value_fill(const Metadata& gmsh_options, MissingValuePolicy policy) { if (policy != MissingValuePolicy::FILL) { return Value{0}; } double fill = 0.; gmsh_options.get("missing_value.fill", fill); return static_cast(fill); } template Value masked_value_fill(const Metadata& gmsh_options, MaskedValuePolicy policy) { if (policy != MaskedValuePolicy::FILL) { return Value{0}; } double fill = 0.; gmsh_options.get("masked_value.fill", fill); return static_cast(fill); } template class ValueHandling { public: ValueHandling(const Metadata& gmsh_options, const Field& field, const Field& mask = Field()): missing_(field), policy_(missing_value_policy(gmsh_options)), missing_fill_(missing_value_fill(gmsh_options, policy_)), masked_policy_(masked_value_policy(gmsh_options)), masked_fill_(masked_value_fill(gmsh_options, masked_policy_)) { if (mask) { ATLAS_ASSERT(mask.contiguous()); mask_ = mask.array().host_data(); } } bool include(const array::LocalView& data, idx_t n) const { if (masked(n)) { if (masked_policy_ == MaskedValuePolicy::SKIP) { return false; } if (masked_policy_ != MaskedValuePolicy::PRESERVE) { return true; } } if (!missing_ || policy_ != MissingValuePolicy::SKIP) { return true; } for (idx_t v = 0; v < data.shape(1); ++v) { if (missing_(data(n, v))) { return false; } } return true; } Value value(Value value, idx_t n) const { if (masked(n)) { if (masked_policy_ == MaskedValuePolicy::FILL) { return masked_fill_; } if (masked_policy_ == MaskedValuePolicy::NOT_A_NUMBER) { if constexpr (std::numeric_limits::has_quiet_NaN) { return std::numeric_limits::quiet_NaN(); } ATLAS_THROW_EXCEPTION("Gmsh masked_value.policy 'nan' requires a floating-point field"); } } if (!missing_ || !missing_(value)) { return value; } if (policy_ == MissingValuePolicy::FILL) { return missing_fill_; } if (policy_ == MissingValuePolicy::NOT_A_NUMBER) { if constexpr (std::numeric_limits::has_quiet_NaN) { return std::numeric_limits::quiet_NaN(); } ATLAS_THROW_EXCEPTION("Gmsh missing_value.policy 'nan' requires a floating-point field"); } return value; } private: bool masked(idx_t n) const { return mask_ && mask_[n] == 0; } field::MissingValue missing_; MissingValuePolicy policy_; Value missing_fill_; MaskedValuePolicy masked_policy_; Value masked_fill_; const int* mask_{nullptr}; }; class NodeInclusionPolicy { public: explicit NodeInclusionPolicy(const mesh::Nodes& nodes): nodes_(nodes.size()) { std::iota(nodes_.begin(), nodes_.end(), idx_t{0}); } const std::vector& included() const { return nodes_; } size_t size() const { return nodes_.size(); } bool empty() const { return nodes_.empty(); } private: std::vector nodes_; }; enum class ElementInclusion { SKIP, OWNED, GHOST }; class ElementInclusionPolicy { public: ElementInclusionPolicy(const Metadata& gmsh_options, int part, bool include_patch, bool coords_is_lonlat, const array::ArrayView& coords, const mesh::Elements& elements): part_(part), include_ghost_(gmsh_options.get("ghost") && gmsh_options.get("elements")), filter_land_water_(gmsh_options.has("water") || gmsh_options.has("land")), include_water_(gmsh_options.getBool("water", false)), include_land_(gmsh_options.getBool("land", false)), include_patch_(include_patch), coords_is_lonlat_(coords_is_lonlat), filter_edge_ratio_(eckit::Resource("$ATLAS_GMSH_FILTER_EDGE_RATIO", 0.)), coords_(coords), elements_(elements), nb_nodes_(elements.element_type().name() == "Pentagon" ? 4 : elements.node_connectivity().cols()), halo_(elements.view(elements.halo())), flags_(elements.view(elements.flags())), partition_(elements.view(elements.partition())) {} ElementInclusion operator()(idx_t elem) const { auto topology = Topology::view(flags_(elem)); if (filter_land_water_ && !(include_water_ && include_land_) && !((include_water_ && topology.check(Topology::WATER)) || (include_land_ && topology.check(Topology::LAND)))) { return ElementInclusion::SKIP; } if (!include_ghost_ && (topology.check(Topology::GHOST) || halo_(elem))) { return ElementInclusion::SKIP; } if (!include_patch_ && topology.check(Topology::PATCH)) { return ElementInclusion::SKIP; } if (topology.check(Topology::INVALID)) { return ElementInclusion::SKIP; } if (coords_is_lonlat_ && nb_nodes_ == 3 && !include_triangle(elements_.node_connectivity(), elem)) { return ElementInclusion::SKIP; } const bool ghost = (topology.check(Topology::GHOST) || halo_(elem)) && partition_(elem) != part_; return ghost ? ElementInclusion::GHOST : ElementInclusion::OWNED; } size_t nb_nodes() const { return nb_nodes_; } private: bool include_triangle(const mesh::BlockConnectivity& connectivity, idx_t elem) const { auto x0 = coords_(connectivity(elem, 0), LON); auto x1 = coords_(connectivity(elem, 1), LON); auto x2 = coords_(connectivity(elem, 2), LON); auto y0 = coords_(connectivity(elem, 0), LAT); auto y1 = coords_(connectivity(elem, 1), LAT); auto y2 = coords_(connectivity(elem, 2), LAT); auto triangle_area = (x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)) * 0.5; if (triangle_area <= 0.) { return false; } if (filter_edge_ratio_ <= 0.) { return true; } auto d10 = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0); auto d21 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1); auto d02 = (x0 - x2) * (x0 - x2) + (y0 - y2) * (y0 - y2); auto dmin = std::min(d10, std::min(d21, d02)); auto dmax = std::max(d10, std::max(d21, d02)); return dmax <= filter_edge_ratio_ * dmin; } int part_; bool include_ghost_; bool filter_land_water_; bool include_water_; bool include_land_; bool include_patch_; bool coords_is_lonlat_; double filter_edge_ratio_; array::ArrayView coords_; const mesh::Elements& elements_; size_t nb_nodes_; array::LocalView halo_; array::LocalView flags_; array::LocalView partition_; }; int field_vars(int nvars) { if (nvars == 1) { return 1; } if (nvars <= 3) { return 3; } if (nvars == 4 || nvars == 9) { return 9; } ATLAS_NOTIMPLEMENTED; } template typename std::remove_const::type output_value(const array::LocalView& data, const ValueHandling& value_handling, idx_t n, int component) { using value_type = typename std::remove_const::type; const int input_vars = data.shape(1); int input_component = component; if (input_vars == 4) { const int row = component / 3; const int col = component % 3; if (row >= 2 || col >= 2) { return value_type{0}; } input_component = row * 2 + col; } if (input_component >= input_vars) { return value_type{0}; } return value_handling.value(data(n, input_component), n); } template void write_ascii_value(std::ostream& out, Value value) { if constexpr (std::is_floating_point_v) { if (std::isnan(value)) { out << "nan"; return; } } out << value; } template void write_level(std::ostream& out, GlobalIndex gidx, const array::LocalView& data, const ValueHandling& value_handling) { const int nvars = field_vars(data.shape(1)); for (idx_t n = 0; n < data.shape(0); ++n) { if (!value_handling.include(data, n)) { continue; } out << gidx(n); for (int v = 0; v < nvars; ++v) { out << " "; write_ascii_value(out, output_value(data, value_handling, n, v)); } out << "\n"; } } template void write_level_binary(std::ostream& out, GlobalIndex gidx, const array::LocalView& data, const ValueHandling& value_handling) { const int nvars = field_vars(data.shape(1)); for (idx_t n = 0; n < data.shape(0); ++n) { if (!value_handling.include(data, n)) { continue; } const int tag = static_cast(gidx(n)); out.write(reinterpret_cast(&tag), sizeof(tag)); for (int v = 0; v < nvars; ++v) { const double value = static_cast(output_value(data, value_handling, n, v)); out.write(reinterpret_cast(&value), sizeof(value)); } } } template idx_t count_output_values(const array::LocalView& data, const ValueHandling& value_handling) { idx_t count = 0; for (idx_t n = 0; n < data.shape(0); ++n) { count += value_handling.include(data, n); } return count; } 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(); } } thread_local std::string field_name_prefix; thread_local Field element_global_index_override; class FieldNamePrefixScope { public: explicit FieldNamePrefixScope(std::string prefix): previous_(std::move(field_name_prefix)) { field_name_prefix = std::move(prefix); } FieldNamePrefixScope(const FieldNamePrefixScope&) = delete; FieldNamePrefixScope& operator=(const FieldNamePrefixScope&) = delete; ~FieldNamePrefixScope() { field_name_prefix = std::move(previous_); } private: std::string previous_; }; class ElementGlobalIndexScope { public: explicit ElementGlobalIndexScope(const Field& global_index): previous_(element_global_index_override) { element_global_index_override = global_index; } ElementGlobalIndexScope(const ElementGlobalIndexScope&) = delete; ElementGlobalIndexScope& operator=(const ElementGlobalIndexScope&) = delete; ~ElementGlobalIndexScope() { element_global_index_override = previous_; } private: Field previous_; }; std::string output_field_name(const Field& field) { return field_name_prefix + field.name(); } 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; } template Field output_mask(const FunctionSpace& function_space, bool gather) { if (!function_space.hasMask()) { return Field(); } Field mask = function_space.mask(); if (gather) { Field global_mask = function_space.createField(mask, option::global()); function_space.gather(mask, global_mask); return global_mask; } return mask; } Field gmsh_edge_tags(const Mesh& mesh) { if (mesh.edges().global_index().metadata().getBool("human_readable",false)) { ATLAS_DEBUG("human_readable edges"); return mesh.edges().global_index(); } ATLAS_DEBUG("non-human_readable edges"); auto& comm = mpi::comm(mesh.mpi_comm()); const int nb_edges = mesh.edges().size(); auto edge_global_index = array::make_view(mesh.edges().global_index()); gidx_t max_edge_tag = -std::numeric_limits::max(); for (int edge = 0; edge < nb_edges; ++edge) { max_edge_tag = std::max(max_edge_tag, edge_global_index(edge)); } comm.allReduceInPlace(max_edge_tag, eckit::mpi::max()); if (max_edge_tag <= std::numeric_limits::max()) { return mesh.edges().global_index(); } Log::warning() << "WARNING: atlas::Gmsh::write(mesh) with edges\n" " Maximum global_index of edges exceeds 32-bit integer limit: " << max_edge_tag << "\n" " This may cause issues with 32-bit indexing in Gmsh $ElementData fields later.\n" " Creating a temporary global_edge_tag array to fit within 32-bit integer range.\n"; std::vector counts(mpi::size()); std::vector displacements(mpi::size()); constexpr int root = 0; comm.gather(nb_edges, counts, root); int nb_global_edges = 0; if (comm.rank() == root) { for (int rank = 0; rank < comm.size(); ++rank) { displacements[rank] = nb_global_edges; nb_global_edges += counts[rank]; } } std::vector global_edge_tags(nb_global_edges); comm.gatherv(edge_global_index.data(), nb_edges, global_edge_tags.data(), counts.data(), displacements.data(), root); if (mpi::rank() == root) { std::vector unique_edge_tags = global_edge_tags; std::sort(unique_edge_tags.begin(), unique_edge_tags.end()); unique_edge_tags.erase(std::unique(unique_edge_tags.begin(), unique_edge_tags.end()), unique_edge_tags.end()); for (gidx_t& tag : global_edge_tags) { tag = std::distance(unique_edge_tags.begin(), std::lower_bound(unique_edge_tags.begin(), unique_edge_tags.end(), tag)) + 1; } } Field tags("gmsh_element_tags", array::make_datatype(), {nb_edges}); auto local_tags = array::make_view(tags); comm.scatterv(global_edge_tags.data(), counts.data(), displacements.data(), local_tags.data(), nb_edges, root); gidx_t local_max_tag = 0; for (int edge = 0; edge < nb_edges; ++edge) { local_max_tag = std::max(local_max_tag, local_tags(edge)); } gidx_t max_tag = 0; comm.allReduce(local_max_tag, max_tag, eckit::mpi::max()); if (max_tag > std::numeric_limits::max()) { ATLAS_THROW_EXCEPTION("Gmsh element tag exceeds the supported 32-bit ElementData range"); } return tags; } } // 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 mask = output_mask(function_space, gather); 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)); } 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); ValueHandling value_handling(gmsh_options, field, mask); const idx_t ndata_output = count_output_values(data, value_handling); out << "$NodeData\n"; out << "1\n"; out << "\"" << output_field_name(field) << 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_output << "\n"; out << mpi::rank() << "\n"; if (gmsh_options.get("ascii")) { write_level(out, gidx, data, value_handling); } else { write_level_binary(out, gidx, data, value_handling); out << "\n"; } 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; }; 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); ValueHandling value_handling(gmsh_options, field); const idx_t ndata_output = count_output_values(data, value_handling); out << "$NodeData\n"; out << "1\n"; out << "\"" << output_field_name(field) << 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_output << "\n"; out << mpi::rank() << "\n"; if (gmsh_options.get("ascii")) { write_level(out, gidx, data, value_handling); } else { write_level_binary(out, gidx, data, value_handling); out << "\n"; } 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); } // ---------------------------------------------------------------------------- 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 mask = output_mask(function_space, gather); 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); } 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); ValueHandling value_handling(gmsh_options, field, mask); const idx_t ndata_output = count_output_values(data, value_handling); out << "$NodeData\n"; out << "1\n"; out << "\"" << output_field_name(field) << 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_output << "\n"; out << mpi::rank() << "\n"; if (gmsh_options.get("ascii")) { write_level(out, gidx, data, value_handling); } else { write_level_binary(out, gidx, data, value_handling); out << "\n"; } out << "$EndNodeData\n"; } } // ---------------------------------------------------------------------------- template void write_field_elems(const Metadata& gmsh_options, const FunctionSpace& function_space, const Field& field, std::ostream& out) { #if 1 Log::debug() << "writing element 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.size(), field.shape(0)); idx_t nvars = std::max(1, field.variables()); Field global_index = element_global_index_override ? element_global_index_override : function_space.global_index(); auto gidx = array::make_view(global_index); Field mask = output_mask(function_space, gather); Field gidx_glb; Field field_glb; if (gather) { gidx_glb = function_space.template createField(option::name("gidx_glb") | option::levels(false) | option::global()); function_space.gather(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); } 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); ValueHandling value_handling(gmsh_options, field, mask); const idx_t ndata_output = count_output_values(data, value_handling); if ((gather && mpi::rank() == 0) || !gather) { out << "$ElementData\n"; out << "1\n"; out << "\"" << output_field_name(field) << 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_output << "\n"; out << mpi::rank() << "\n"; if (gmsh_options.get("ascii")) { write_level(out, gidx, data, value_handling); } else { write_level_binary(out, gidx, data, value_handling); out << "\n"; } out << "$EndElementData\n"; } } #endif } // ---------------------------------------------------------------------------- } // end anonymous namespace // ---------------------------------------------------------------------------- GmshIO::GmshIO() { // which field holds the Nodes options.set("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", false); // Use the zero-based element owner as the elementary entity tag options.set("element_partition_as_entity", false); // Levels of fields to use options.set>("levels", std::vector()); options.set("missing_value.policy", "skip"); options.set("missing_value.fill", 0.); options.set("masked_value.policy", "skip"); options.set("masked_value.fill", 0.); } 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(); 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"; 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); NodeInclusionPolicy node_inclusion(nodes); ATLAS_ASSERT(surfdim == 2 || surfdim == 3); Log::debug() << "writing mesh to gmsh file " << file_path << std::endl; const bool binary = !options.get("ascii"); const bool element_partition_as_entity = options.get("element_partition_as_entity"); Field edge_element_tags; if (options.get("edges")) { edge_element_tags = mesh.edges().global_index(); if (options.has("info") && options.get("info")) { edge_element_tags = gmsh_edge_tags(mesh); } } 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); } struct ElementBlock { const mesh::Elements* elements; const Field* global_index; int dimension; int gmsh_type; size_t nb_nodes; std::vector included; std::map> ghosts_by_owner; std::map> by_owner; }; std::vector element_blocks; std::vector grouped_elements; if (options.get("elements")) { grouped_elements.push_back(&mesh.cells()); } if (options.get("edges")) { grouped_elements.push_back(&mesh.edges()); } 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(); ElementInclusionPolicy element_inclusion(options, part, include_patch, coords_is_lonlat, coords, elements); const Field& element_global_index = hybrid == &mesh.edges() ? edge_element_tags : hybrid->global_index(); ElementBlock block{&elements, &element_global_index, 2, 0, element_inclusion.nb_nodes(), {}, {}, {}}; auto element_owner = elements.view(elements.partition()); if (element_type.name() == "Line") { block.dimension = 1; block.gmsh_type = LINE; } else if (element_type.name() == "Triangle") { block.gmsh_type = TRIAG; } else if (element_type.name() == "Quadrilateral") { block.gmsh_type = QUAD; } else if (element_type.name() == "Pentagon") { block.gmsh_type = QUAD; } else { ATLAS_NOTIMPLEMENTED; } std::vector inclusion(elements.size()); std::map owner_counts; size_t included_count = 0; for (idx_t elem = 0; elem < elements.size(); ++elem) { inclusion[elem] = element_inclusion(elem); switch (inclusion[elem]) { case ElementInclusion::OWNED: element_partition_as_entity ? ++owner_counts[element_owner(elem)] : ++included_count; break; case ElementInclusion::GHOST: ++owner_counts[element_owner(elem)]; break; case ElementInclusion::SKIP: break; } } if (element_partition_as_entity) { for (const auto& [owner, count] : owner_counts) { block.by_owner[owner].reserve(count); } } else { block.included.reserve(included_count); for (const auto& [owner, count] : owner_counts) { block.ghosts_by_owner[owner].reserve(count); } } for (idx_t elem = 0; elem < elements.size(); ++elem) { switch (inclusion[elem]) { case ElementInclusion::OWNED: (element_partition_as_entity ? block.by_owner[element_owner(elem)] : block.included) .push_back(elem); break; case ElementInclusion::GHOST: (element_partition_as_entity ? block.by_owner[element_owner(elem)] : block.ghosts_by_owner[element_owner(elem)]) .push_back(elem); break; case ElementInclusion::SKIP: break; } } if (element_partition_as_entity ? !block.by_owner.empty() : !block.included.empty() || !block.ghosts_by_owner.empty()) { element_blocks.emplace_back(std::move(block)); } } } const idx_t nb_nodes = static_cast(node_inclusion.size()); const int partition_tag = part; const int canonical_entity_tag = part + 2; const int node_entity_tag = element_partition_as_entity ? partition_tag : canonical_entity_tag; const int nb_partitions = std::max(part + 1, mesh.metadata().has("nb_parts") ? mesh.metadata().get("nb_parts") : static_cast(mpi::size())); const bool has_curve_blocks = std::any_of(element_blocks.begin(), element_blocks.end(), [](const auto& block) { return block.dimension == 1; }); std::map curve_entity_owners; std::map surface_entity_owners; if (element_partition_as_entity) { if (nb_nodes) { surface_entity_owners[partition_tag] = true; } for (const auto& block : element_blocks) { auto& owners = block.dimension == 1 ? curve_entity_owners : surface_entity_owners; for ([[maybe_unused]] const auto& [owner, elements] : block.by_owner) { owners[owner] = true; } } } else { if (nb_nodes) { surface_entity_owners[part] = true; } for (const auto& block : element_blocks) { auto& owners = block.dimension == 1 ? curve_entity_owners : surface_entity_owners; if (!block.included.empty()) { owners[part] = true; } for ([[maybe_unused]] const auto& [owner, elements] : block.ghosts_by_owner) { owners[owner] = true; } } } const size_t nb_ghost_entities = 0; double xyz_min[3] = {std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()}; double xyz_max[3] = {-std::numeric_limits::max(), -std::numeric_limits::max(), -std::numeric_limits::max()}; auto node_xyz = [&](idx_t node) { std::array xyz{0., 0., 0.}; for (idx_t dimension = 0; dimension < surfdim; ++dimension) { xyz[dimension] = coords_is_idx ? coords_idx(node, dimension) : coords(node, dimension); } return xyz; }; for (idx_t node : node_inclusion.included()) { auto xyz = node_xyz(node); for (idx_t dimension = 0; dimension < 3; ++dimension) { xyz_min[dimension] = std::min(xyz_min[dimension], xyz[dimension]); xyz_max[dimension] = std::max(xyz_max[dimension], xyz[dimension]); } } if (nb_nodes == 0) { std::fill(xyz_min, xyz_min + 3, 0.); std::fill(xyz_max, xyz_max + 3, 0.); } auto write_bounding_box = [&]() { file << xyz_min[0] << " " << xyz_min[1] << " " << xyz_min[2] << " " << xyz_max[0] << " " << xyz_max[1] << " " << xyz_max[2]; }; auto write_binary = [&](const auto& value) { file.write(reinterpret_cast(&value), sizeof(value)); }; auto write_binary_bounding_box = [&]() { file.write(reinterpret_cast(xyz_min), sizeof(xyz_min)); file.write(reinterpret_cast(xyz_max), sizeof(xyz_max)); }; file << "$Entities\n"; if (binary) { const size_t entity_counts[4] = { 0, element_partition_as_entity ? curve_entity_owners.size() : (has_curve_blocks ? 1ul : 0ul), element_partition_as_entity ? surface_entity_owners.size() : 1ul, 0}; file.write(reinterpret_cast(entity_counts), sizeof(entity_counts)); auto write_entity_record = [&](int tag) { const size_t nb_physical = 1; const int physical_tag = 1; const size_t nb_boundaries = 0; write_binary(tag); write_binary_bounding_box(); write_binary(nb_physical); write_binary(physical_tag); write_binary(nb_boundaries); }; if (element_partition_as_entity) { for ([[maybe_unused]] const auto& [owner, present] : curve_entity_owners) { write_entity_record(owner); } for ([[maybe_unused]] const auto& [owner, present] : surface_entity_owners) { write_entity_record(owner); } } else { if (has_curve_blocks) { write_entity_record(1); // parent curve entity } write_entity_record(1); // parent surface entity } file << "\n"; } else { file << "0 " << (element_partition_as_entity ? curve_entity_owners.size() : (has_curve_blocks ? 1 : 0)) << " " << (element_partition_as_entity ? surface_entity_owners.size() : 1) << " 0\n"; auto write_entity_record = [&](int tag) { file << tag << " "; write_bounding_box(); file << " 1 1 0\n"; }; if (element_partition_as_entity) { for ([[maybe_unused]] const auto& [owner, present] : curve_entity_owners) { write_entity_record(owner); } for ([[maybe_unused]] const auto& [owner, present] : surface_entity_owners) { write_entity_record(owner); } } else { if (has_curve_blocks) { write_entity_record(1); } write_entity_record(1); } } file << "$EndEntities\n"; constexpr int dim_1d = 1; constexpr int dim_2d = 2; if (!element_partition_as_entity) { file << "$PartitionedEntities\n"; if (binary) { const size_t partitions = nb_partitions; const size_t entity_counts[4] = {0, curve_entity_owners.size(), surface_entity_owners.size(), 0}; write_binary(partitions); write_binary(nb_ghost_entities); file.write(reinterpret_cast(entity_counts), sizeof(entity_counts)); auto write_partitioned_entity = [&](int owner, int dimension) { const int parent_tag_value = 1; const size_t entity_nb_partitions = 1; const size_t nb_physical = 1; const int physical_tag = 1; const size_t nb_boundaries = 0; write_binary(owner + 2); write_binary(dimension); write_binary(parent_tag_value); write_binary(entity_nb_partitions); write_binary(owner); write_binary_bounding_box(); write_binary(nb_physical); write_binary(physical_tag); write_binary(nb_boundaries); }; for ([[maybe_unused]] const auto& [owner, present] : curve_entity_owners) { write_partitioned_entity(owner, dim_1d); } for ([[maybe_unused]] const auto& [owner, present] : surface_entity_owners) { write_partitioned_entity(owner, dim_2d); } file << "\n"; } else { file << nb_partitions << "\n"; file << nb_ghost_entities << "\n"; file << "0 " << curve_entity_owners.size() << " " << surface_entity_owners.size() << " 0\n"; auto write_partitioned_entity = [&](int owner, int dimension) { file << owner + 2 << " " << dimension << " 1 1 " << owner << " "; write_bounding_box(); file << " 1 1 0\n"; }; for ([[maybe_unused]] const auto& [owner, present] : curve_entity_owners) { write_partitioned_entity(owner, dim_1d); } for ([[maybe_unused]] const auto& [owner, present] : surface_entity_owners) { write_partitioned_entity(owner, dim_2d); } } file << "$EndPartitionedEntities\n"; } gidx_t min_node_tag = node_inclusion.empty() ? 0 : glb_idx(node_inclusion.included().front()); gidx_t max_node_tag = min_node_tag; for (idx_t node : node_inclusion.included()) { min_node_tag = std::min(min_node_tag, glb_idx(node)); max_node_tag = std::max(max_node_tag, glb_idx(node)); } file << "$Nodes\n"; if (binary) { const size_t node_header[4] = {nb_nodes ? 1ul : 0ul, static_cast(nb_nodes), static_cast(min_node_tag), static_cast(max_node_tag)}; file.write(reinterpret_cast(node_header), sizeof(node_header)); if (nb_nodes) { const int dimension = 2; const int parametric = 0; const size_t count = nb_nodes; write_binary(dimension); write_binary(node_entity_tag); write_binary(parametric); write_binary(count); for (idx_t node : node_inclusion.included()) { const size_t tag = glb_idx(node); write_binary(tag); } for (idx_t node : node_inclusion.included()) { auto xyz = node_xyz(node); file.write(reinterpret_cast(xyz.data()), sizeof(double) * xyz.size()); } } file << "\n"; } else { file << (nb_nodes ? 1 : 0) << " " << nb_nodes << " " << min_node_tag << " " << max_node_tag << "\n"; if (nb_nodes) { file << "2 " << node_entity_tag << " 0 " << nb_nodes << "\n"; for (idx_t node : node_inclusion.included()) { file << glb_idx(node) << "\n"; } for (idx_t node : node_inclusion.included()) { auto xyz = node_xyz(node); file << xyz[0] << " " << xyz[1] << " " << xyz[2] << "\n"; } } } file << "$EndNodes\n"; size_t nb_elements = 0; gidx_t min_element_tag = 0; gidx_t max_element_tag = 0; for (const auto& block : element_blocks) { auto elems_glb_idx = block.elements->view(*block.global_index); auto update_element_range = [&](idx_t elem) { const gidx_t tag = elems_glb_idx(elem); if (nb_elements++ == 0) { min_element_tag = tag; max_element_tag = tag; } else { min_element_tag = std::min(min_element_tag, tag); max_element_tag = std::max(max_element_tag, tag); } }; if (element_partition_as_entity) { for (const auto& [owner, elements] : block.by_owner) { for (idx_t elem : elements) { update_element_range(elem); } } } else { for (idx_t elem : block.included) { update_element_range(elem); } for (const auto& [owner, elements] : block.ghosts_by_owner) { for (idx_t elem : elements) { update_element_range(elem); } } } } const size_t nb_element_blocks = std::accumulate( element_blocks.begin(), element_blocks.end(), size_t{0}, [&](size_t count, const auto& block) { return count + (element_partition_as_entity ? block.by_owner.size() : !block.included.empty() + block.ghosts_by_owner.size()); }); file << "$Elements\n"; if (binary) { const size_t element_header[4] = {nb_element_blocks, nb_elements, static_cast(min_element_tag), static_cast(max_element_tag)}; file.write(reinterpret_cast(element_header), sizeof(element_header)); for (const auto& block : element_blocks) { const auto& node_connectivity = block.elements->node_connectivity(); auto elems_glb_idx = block.elements->view(*block.global_index); auto write_block = [&](const std::vector& elements, int block_entity_tag) { if (elements.empty()) { return; } const size_t count = elements.size(); write_binary(block.dimension); write_binary(block_entity_tag); write_binary(block.gmsh_type); write_binary(count); for (idx_t elem : elements) { const size_t element_tag = elems_glb_idx(elem); write_binary(element_tag); for (idx_t node = 0; node < block.nb_nodes; ++node) { const size_t node_tag = glb_idx(node_connectivity(elem, node)); write_binary(node_tag); } } }; if (element_partition_as_entity) { for (const auto& [owner, elements] : block.by_owner) { write_block(elements, owner); } } else { write_block(block.included, canonical_entity_tag); for (const auto& [owner, elements] : block.ghosts_by_owner) { write_block(elements, owner + 2); } } } file << "\n"; } else { file << nb_element_blocks << " " << nb_elements << " " << min_element_tag << " " << max_element_tag << "\n"; for (const auto& block : element_blocks) { const auto& node_connectivity = block.elements->node_connectivity(); auto elems_glb_idx = block.elements->view(*block.global_index); auto write_block = [&](const std::vector& elements, int block_entity_tag) { if (elements.empty()) { return; } file << block.dimension << " " << block_entity_tag << " " << block.gmsh_type << " " << elements.size() << "\n"; for (idx_t elem : elements) { file << elems_glb_idx(elem); for (idx_t node = 0; node < block.nb_nodes; ++node) { file << " " << glb_idx(node_connectivity(elem, node)); } file << "\n"; } }; if (element_partition_as_entity) { for (const auto& [owner, elements] : block.by_owner) { write_block(elements, owner); } } else { write_block(block.included, canonical_entity_tag); for (const auto& [owner, elements] : block.ghosts_by_owner) { write_block(elements, owner + 2); } } } } 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"; const std::vector extra_fields = {"partition", "water", "dual_volumes", "dual_delta_sph", "ghost", "halo", "remote_index"}; { functionspace::NodeColumns function_space(mesh); FieldNamePrefixScope field_name_prefix_scope("nodes."); 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); for (const auto& field_name : extra_fields) { if (nodes.has_field(field_name)) { write(nodes.field(field_name), function_space, mesh_info, std::ios_base::app); } } } if (options.get("elements")) { functionspace::CellColumns function_space(mesh); FieldNamePrefixScope field_name_prefix_scope("cells."); for (const auto& field_name : extra_fields) { if (mesh.cells().has_field(field_name)) { write(mesh.cells().field(field_name), function_space, mesh_info, std::ios_base::app); } } } if (options.get("edges") && mesh.edges().size()) { functionspace::EdgeColumns function_space(mesh); FieldNamePrefixScope field_name_prefix_scope("edges."); ElementGlobalIndexScope element_global_index_scope(edge_element_tags); for (const auto& field_name : extra_fields) { if (mesh.edges().has_field(field_name)) { write(mesh.edges().field(field_name), function_space, mesh_info, std::ios_base::app); } } } } 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 if (functionspace::EdgeColumns(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 or functionspace::CellColumns " << "or functionspace::EdgeColumns]"; 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::EdgeColumns& 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) { binary ? write_header_binary(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) { binary ? write_header_binary(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) { binary ? write_header_binary(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::EdgeColumns& 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())); if (is_new_file) { binary ? write_header_binary(file) : write_header_ascii(file); } 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) { binary ? write_header_binary(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 (functionspace::EdgeColumns(funcspace)) { write_delegate(fieldset, functionspace::EdgeColumns(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 if (functionspace::EdgeColumns(funcspace)) { write_delegate(field, functionspace::EdgeColumns(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 ecmwf-atlas-b0f79ee/src/atlas/output/Gmsh.cc0000664000175000017500000000570015263665111021254 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/output/Gmsh.h0000664000175000017500000000325215263665111021116 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/output/Output.cc0000664000175000017500000001740615263665111021664 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/output/Output.h0000664000175000017500000001333315263665111021521 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/0000775000175000017500000000000015263665111017623 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/array/IndexView.h0000664000175000017500000000306015263665111021675 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/array/ArraySpec.cc0000664000175000017500000001474115263665111022032 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/array/Table.cc0000664000175000017500000003003415263665111021161 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/array/SVector.h0000664000175000017500000001144315263665111021364 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/Vector.cc0000664000175000017500000000101615263665111021372 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/array/helpers/0000775000175000017500000000000015263665111021265 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/array/helpers/ArrayInitializer.h0000664000175000017500000002326515263665111024730 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/helpers/ArrayCopier.h0000664000175000017500000000627015263665111023663 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 ecmwf-atlas-b0f79ee/src/atlas/array/helpers/ArrayForEach.h0000664000175000017500000003310215263665111023743 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 ecmwf-atlas-b0f79ee/src/atlas/array/helpers/ArrayAssigner.h0000664000175000017500000000667615263665111024227 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/helpers/ArraySlicer.h0000664000175000017500000002726015263665111023665 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/helpers/ArrayWriter.h0000664000175000017500000000445415263665111023720 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/ArrayViewDefs.h0000664000175000017500000000164015263665111022510 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/TableView.h0000664000175000017500000001727415263665111021671 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/array/ArrayViewVariant.cc0000664000175000017500000000637515263665111023403 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 ecmwf-atlas-b0f79ee/src/atlas/array/TableView.cc0000664000175000017500000000537615263665111022027 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/array/ArrayViewUtil.h0000664000175000017500000000354315263665111022550 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/MakeView.h0000664000175000017500000000722115263665111021506 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/ArrayIdx.h0000664000175000017500000000313215263665111021516 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/SVector.cc0000664000175000017500000000121515263665111021516 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/array/LocalView.cc0000664000175000017500000001446215263665111022026 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/array/DataType.h0000664000175000017500000000067015263665111021512 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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" ecmwf-atlas-b0f79ee/src/atlas/array/ArrayDataStore.cc0000664000175000017500000000146215263665111023022 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/array/Table.h0000664000175000017500000001402215263665111021022 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/array/ArraySpec.h0000664000175000017500000000657515263665111021702 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/gridtools/0000775000175000017500000000000015263665111021631 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/array/gridtools/GridToolsArrayView.cc0000664000175000017500000002204015263665111025676 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/array/gridtools/GridToolsIndexView.cc0000664000175000017500000001467415263665111025705 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/array/gridtools/GridToolsDataStore.h0000664000175000017500000000763715263665111025534 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/gridtools/GridToolsArray.cc0000664000175000017500000006257515263665111025064 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/array/gridtools/GridToolsArrayHelpers.h0000664000175000017500000002440715263665111026241 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/gridtools/GridToolsMakeView.cc0000664000175000017500000003740115263665111025504 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/array/gridtools/GridToolsTraits.h0000664000175000017500000000442615263665111025105 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/gridtools/GridToolsMakeView.h0000664000175000017500000000224215263665111025341 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/gridtools/GridToolsArrayView.h0000664000175000017500000001576715263665111025562 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/gridtools/GridToolsIndexView.h0000664000175000017500000001536315263665111025543 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/native/0000775000175000017500000000000015263665111021111 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/array/native/NativeMakeView.cc0000664000175000017500000001566715263665111024316 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/array/native/NativeArrayView.cc0000664000175000017500000001571715263665111024513 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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(unsigned long 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&); \ 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&); \ 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 ecmwf-atlas-b0f79ee/src/atlas/array/native/NativeArrayView.h0000664000175000017500000003606615263665111024355 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 number of values in dimension idx, equivalent to shape(idx) template ATLAS_HOST_DEVICE idx_t extent(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 ecmwf-atlas-b0f79ee/src/atlas/array/native/NativeArray.cc0000664000175000017500000003166215263665111023655 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/array/native/NativeDataStore.h0000664000175000017500000005217615263665111024332 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/native/NativeIndexView.cc0000664000175000017500000000536115263665111024476 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/array/native/NativeIndexView.h0000664000175000017500000001750215263665111024340 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/array/ArrayDataStore.h0000664000175000017500000000575315263665111022673 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/ArrayViewVariant.h0000664000175000017500000000665015263665111023241 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 ecmwf-atlas-b0f79ee/src/atlas/array/Vector.h0000664000175000017500000000766115263665111021250 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/ArrayStrides.h0000664000175000017500000000501615263665111022412 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/LocalView.h0000664000175000017500000002552015263665111021665 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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]; } /// @brief Return number of values in dimension idx, equivalent to shape(idx) template ATLAS_HOST_DEVICE idx_t extent(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 ecmwf-atlas-b0f79ee/src/atlas/array/Range.h0000664000175000017500000000605115263665111021032 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/ArrayShape.h0000664000175000017500000000576715263665111022052 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/ArrayView.h0000664000175000017500000000324715263665111021713 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/ArrayLayout.h0000664000175000017500000000341515263665111022253 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array/Array.h0000664000175000017500000002160415263665111021055 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/0000775000175000017500000000000015263665111017432 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/grid/detail/0000775000175000017500000000000015263665111020674 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/0000775000175000017500000000000015263665111021621 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/LonLat.h0000664000175000017500000000064015263665111023163 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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" ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/Unstructured.cc0000664000175000017500000002065515263665111024647 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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__x_y(size_t size, const double x[], const double y[], size_t xstride, size_t ystride) { return new Unstructured(size, x, y, xstride, ystride); } 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/LonLat.cc0000664000175000017500000002226115263665111023324 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/CubedSphere2.cc0000664000175000017500000001335315263665111024410 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/Structured.h0000664000175000017500000003605715263665111024151 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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_; bool domain_set_{false}; 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/GridBuilder.h0000664000175000017500000000335215263665111024171 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/Unstructured.h0000664000175000017500000002050115263665111024477 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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__x_y(size_t size, const double x[], const double y[], size_t xstride, size_t ystride); const Unstructured* atlas__grid__Unstructured__config(util::Config* conf); } } // namespace grid } // namespace detail } // namespace grid } // namespace atlas ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/CubedSphere2.h0000664000175000017500000001357315263665111024256 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/GridFactory.cc0000664000175000017500000000277015263665111024353 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/Grid.h0000664000175000017500000001465415263665111022671 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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/grid/GridPointFlags.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; /// @return intrinsic properties of a grid point virtual int flags(gidx_t) const { return GridPointFlags::none; } /// @return zero-based index of the grid point that owns the given point virtual gidx_t masterIndex(gidx_t index) const { return index; } 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" { const Grid* atlas__grid__Grid(const char* identifier); const Grid* atlas__grid__Grid__config(util::Config* conf); void atlas__grid__Grid__delete(Grid* This); void atlas__grid__Grid__name(const Grid* This, char*& uid, int& size); void atlas__grid__Grid__type(const Grid* This, char*& type, 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/RegionalVariableResolution.h0000664000175000017500000000033615263665111027266 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" ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/GridBuilder.cc0000664000175000017500000001675215263665111024337 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/CubedSphere.h0000664000175000017500000003754015263665111024174 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/Regional.cc0000664000175000017500000003067015263665111023676 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/StencilComputerInterface.h0000664000175000017500000000252315263665111026735 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); } ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/Healpix.cc0000664000175000017500000001002715263665111023522 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/GridFactory.h0000664000175000017500000000304315263665111024207 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/Gaussian.cc0000664000175000017500000002141115263665111023701 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/Gaussian.h0000664000175000017500000000211715263665111023545 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/Regional.h0000664000175000017500000000064015263665111023532 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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" ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/CubedSphere.cc0000664000175000017500000005134115263665111024325 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/StencilComputerInterface.cc0000664000175000017500000000327515263665111027100 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); } } ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/RegionalVariableResolution.cc0000664000175000017500000001074315263665111027427 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/Grid.cc0000664000175000017500000001535315263665111023024 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 #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 (config.get("grid", name)) { // This is an eckit::geo grid specification. // For now simply treat it as name and hope for the best... // It should work for all named grids return create(name, 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& from_string) { return create(from_string, util::NoConfig()); } const Grid* Grid::create(const std::string& from_string, const Grid::Config& config) { const auto first = from_string.find_first_not_of(" \t\n\r"); if (first != std::string::npos && from_string[first] == '{') { std::istringstream stream(from_string); return create(util::Config(stream, "json")); } for (const auto& [key, builder]: GridBuilder::nameRegistry()) { const Grid* grid = builder->create(from_string, config); if (grid) { return grid; } } // Throw exception std::ostringstream log; log << "Could not construct Grid from the name \"" << from_string << "\"\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; } const Grid* atlas__grid__Grid(const char* identifier) { return Grid::create(std::string(identifier)); } const Grid* atlas__grid__Grid__config(util::Config* conf){ return Grid::create(*conf); } 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); } void atlas__grid__Grid__type(const Grid* This, char*& type, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Grid"); std::string s = This->type(); size = static_cast(s.size()); type = new char[size + 1]; std::strncpy(type, 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/Structured.cc0000664000175000017500000007344315263665111024307 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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), domain_set_(bool(domain)) { // 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()); } if (domain_set_) { grid_spec.set("domain", domain().spec()); } if (projection()) { 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/grid/Healpix.h0000664000175000017500000000171115263665111023364 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/0000775000175000017500000000000015263665111023234 5ustar alastairalastair././@LongLink0000644000000000000000000000014700000000000011605 Lustar rootrootecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitionerLonLatPolygon.ccecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitionerLonLatPolygon.0000664000175000017500000001132615263665111034357 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/RegularBandsPartitioner.cc0000664000175000017500000000167315263665111030344 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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()); } ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/BandsPartitioner.cc0000664000175000017500000000521215263665111027013 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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()); } ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/BandsPartitioner.h0000664000175000017500000000313415263665111026656 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/Partitioner.h0000664000175000017500000000737615263665111025722 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.h0000664000175000017500000000345115263665111032662 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.h0000664000175000017500000001252115263665111030225 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerSphericalPolygon.cc0000664000175000017500000000600615263665111033540 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/SerialPartitioner.cc0000664000175000017500000000171015263665111027202 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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_); } } } } } ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/TransPartitioner.h0000664000175000017500000000323615263665111026721 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/CheckerboardPartitioner.cc0000664000175000017500000002054215263665111030343 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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"); } ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/MatchingMeshPartitioner.h0000664000175000017500000000176515263665111030206 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/EqualAreaPartitioner.cc0000664000175000017500000000453715263665111027635 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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"); } ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerCubedSphere.cc0000664000175000017500000000525115263665111032450 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/CubedSpherePartitioner.cc0000664000175000017500000002365015263665111030163 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 isPerfectSquare(const atlas::idx_t value, atlas::idx_t& square_root) { if (value <= 0) { square_root = 0; return false; } square_root = 1; while ((square_root + 1) <= (value / (square_root + 1))) { ++square_root; } return (value % square_root == 0) && (value / square_root == square_root); } 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) { idx_t square_root = 0; if (isPerfectSquare(cb.nproc[t], square_root)) { cb.nprocx[t] = square_root; cb.nprocy[t] = square_root; } 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] ? 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"); } ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerCubedSphere.h0000664000175000017500000000176015263665111032313 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 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/CheckerboardPartitioner.h0000664000175000017500000000435615263665111030212 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ././@LongLink0000644000000000000000000000014600000000000011604 Lustar rootrootecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitionerLonLatPolygon.hecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitionerLonLatPolygon.0000664000175000017500000000333015263665111034353 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerSphericalPolygon.h0000664000175000017500000000355715263665111033412 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/SerialPartitioner.h0000664000175000017500000000315315263665111027047 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/RegularBandsPartitioner.h0000664000175000017500000000220615263665111030177 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/EqualBandsPartitioner.cc0000664000175000017500000000165515263665111030012 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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()); } ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitioner.h0000664000175000017500000000207715263665111032050 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/TransPartitioner.cc0000664000175000017500000000750715263665111027064 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/CubedSpherePartitioner.h0000664000175000017500000001005515263665111030020 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.cc0000664000175000017500000001600515263665111033017 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerBruteForce.h0000664000175000017500000000272715263665111032166 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/EqualAreaPartitioner.h0000664000175000017500000000224715263665111027473 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/MatchingMeshPartitioner.cc0000664000175000017500000000227115263665111030335 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/Partitioner.cc0000664000175000017500000002242615263665111026051 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerBruteForce.cc0000664000175000017500000001566715263665111032333 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitioner.cc0000664000175000017500000000254115263665111032202 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.cc0000664000175000017500000010654315263665111030373 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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"); } ecmwf-atlas-b0f79ee/src/atlas/grid/detail/partitioner/EqualBandsPartitioner.h0000664000175000017500000000215415263665111027647 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/tiles/0000775000175000017500000000000015263665111022014 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/grid/detail/tiles/TilesFactory.h0000664000175000017500000000326415263665111024602 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/tiles/LFRicTiles.h0000664000175000017500000000433315263665111024130 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/tiles/Tiles.cc0000664000175000017500000000327415263665111023411 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/tiles/LFRicTiles.cc0000664000175000017500000006766315263665111024305 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/tiles/TilesFactory.cc0000664000175000017500000000334315263665111024736 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/tiles/FV3Tiles.cc0000664000175000017500000003342315263665111023727 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/tiles/FV3Tiles.h0000664000175000017500000000332015263665111023562 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/tiles/Tiles.h0000664000175000017500000000431415263665111023247 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/vertical/0000775000175000017500000000000015263665111022505 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/grid/detail/vertical/VerticalInterface.h0000664000175000017500000000170215263665111026250 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/vertical/VerticalInterface.cc0000664000175000017500000000311515263665111026406 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/0000775000175000017500000000000015263665111021307 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/0000775000175000017500000000000015263665111024622 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N.h0000664000175000017500000000717415263665111025201 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N96.cc0000664000175000017500000000245015263665111025506 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N32.cc0000664000175000017500000000152615263665111025477 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N400.cc0000664000175000017500000000601715263665111025556 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N2000.cc0000664000175000017500000003227015263665111025634 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N8000.cc0000664000175000017500000016326715263665111025655 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N256.cc0000664000175000017500000000417615263665111025573 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N640.cc0000664000175000017500000001105215263665111025557 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N.cc0000664000175000017500000000347115263665111025333 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N64.cc0000664000175000017500000000210015263665111025471 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N320.cc0000664000175000017500000000477215263665111025565 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/PointsPerLatitude.cc0000664000175000017500000000463115263665111030554 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N4000.cc0000664000175000017500000007030615263665111025640 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/PointsPerLatitude.h0000664000175000017500000000344015263665111030413 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N160.cc0000664000175000017500000000340115263665111025553 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N16.cc0000664000175000017500000000134015263665111025473 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N80.cc0000664000175000017500000000226415263665111025502 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N200.cc0000664000175000017500000000331515263665111025552 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N128.cc0000664000175000017500000000303015263665111025555 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N1024.cc0000664000175000017500000001576315263665111025651 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N576.cc0000664000175000017500000001021615263665111025570 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N24.cc0000664000175000017500000000144415263665111025477 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N48.cc0000664000175000017500000000171315263665111025504 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N1600.cc0000664000175000017500000002522315263665111025641 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N800.cc0000664000175000017500000001307415263665111025563 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N1280.cc0000664000175000017500000002116115263665111025642 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/pl/classic_gaussian/N512.cc0000664000175000017500000000735115263665111025564 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/distribution/0000775000175000017500000000000015263665111023413 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/grid/detail/distribution/DistributionFunction.h0000664000175000017500000000435315263665111027756 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/distribution/BandsDistribution.cc0000664000175000017500000000616215263665111027356 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/distribution/DistributionImpl.cc0000664000175000017500000000510415263665111027223 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/distribution/DistributionImpl.h0000664000175000017500000000473415263665111027075 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/distribution/DistributionArray.h0000664000175000017500000000463515263665111027252 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/distribution/SerialDistribution.cc0000664000175000017500000000231715263665111027544 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/distribution/BandsDistribution.h0000664000175000017500000000233515263665111027216 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/distribution/SerialDistribution.h0000664000175000017500000000164615263665111027412 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/distribution/DistributionFunction.cc0000664000175000017500000000243715263665111030115 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/distribution/DistributionArray.cc0000664000175000017500000001062215263665111027401 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/0000775000175000017500000000000015263665111022320 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/CustomSpacing.cc0000664000175000017500000000355215263665111025413 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/FocusSpacing.h0000664000175000017500000000167715263665111025070 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/GaussianSpacing.cc0000664000175000017500000000430015263665111025703 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/Spacing.cc0000664000175000017500000000166515263665111024223 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/SpacingFactory.h0000664000175000017500000000307615263665111025413 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/0000775000175000017500000000000015263665111024132 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N.h0000664000175000017500000000645415263665111024511 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N96.cc0000664000175000017500000000466015263665111025023 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N32.cc0000664000175000017500000000260015263665111025001 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N400.cc0000664000175000017500000001751215263665111025070 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N2000.cc0000664000175000017500000011103115263665111025135 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N8000.cc0000664000175000017500000044020715263665111025155 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N256.cc0000664000175000017500000001271115263665111025075 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/Latitudes.h0000664000175000017500000000401415263665111026240 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N640.cc0000664000175000017500000003021015263665111025064 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N.cc0000664000175000017500000000323315263665111024637 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N64.cc0000664000175000017500000000416015263665111025011 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N320.cc0000664000175000017500000001461515263665111025072 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N4000.cc0000664000175000017500000022062315263665111025147 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N160.cc0000664000175000017500000000722315263665111025071 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N16.cc0000664000175000017500000000211115263665111025000 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N80.cc0000664000175000017500000000474715263665111025022 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N200.cc0000664000175000017500000001035715263665111025066 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N128.cc0000664000175000017500000000606115263665111025074 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N1024.cc0000664000175000017500000004602515263665111025154 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N576.cc0000664000175000017500000002575415263665111025115 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N24.cc0000664000175000017500000000246615263665111025014 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/Latitudes.cc0000664000175000017500000002347615263665111026413 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N48.cc0000664000175000017500000000337015263665111025015 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N1600.cc0000664000175000017500000007254315263665111025160 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N800.cc0000664000175000017500000003600015263665111025065 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N1280.cc0000664000175000017500000005716315263665111025165 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/gaussian/N512.cc0000664000175000017500000002353115263665111025072 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/CustomSpacing.h0000664000175000017500000000411115263665111025245 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/Spacing.h0000664000175000017500000000322015263665111024052 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/FocusSpacing.cc0000664000175000017500000000464015263665111025217 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/LinearSpacing.h0000664000175000017500000000553315263665111025216 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/GaussianSpacing.h0000664000175000017500000000315715263665111025556 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/SpacingFactory.cc0000664000175000017500000000256215263665111025550 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/detail/spacing/LinearSpacing.cc0000664000175000017500000000741415263665111025354 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/Partitioner.h0000664000175000017500000000753615263665111022116 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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__MatchingPartitioner__new(const util::Object* object, const Partitioner::Config* config); 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 ecmwf-atlas-b0f79ee/src/atlas/grid/Spacing.cc0000664000175000017500000000402715263665111021330 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/CubedSphereGrid.cc0000664000175000017500000000214115263665111022736 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 ecmwf-atlas-b0f79ee/src/atlas/grid/UnstructuredGrid.cc0000664000175000017500000000404615263665111023262 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/Iterator.cc0000664000175000017500000000167615263665111021544 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/SpecRegistry.h0000664000175000017500000000223615263665111022231 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/Stencil.h0000664000175000017500000000510615263665111021206 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/Vertical.h0000664000175000017500000000553015263665111021357 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/Vertical.cc0000664000175000017500000000352715263665111021521 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/Grid.h0000664000175000017500000000700215263665111020467 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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/grid/GridPointFlags.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; int flags(gidx_t index) const; gidx_t masterIndex(gidx_t index) 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 ecmwf-atlas-b0f79ee/src/atlas/grid/CubedSphereGrid2.cc0000664000175000017500000000133515263665111023024 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 ecmwf-atlas-b0f79ee/src/atlas/grid/Spacing.h0000664000175000017500000000530015263665111021165 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/StructuredPartitionPolygon.h0000664000175000017500000000426115263665111025214 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/StructuredGrid.cc0000664000175000017500000000655315263665111022724 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/CubedSphereGrid2.h0000664000175000017500000000101115263665111022655 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 ecmwf-atlas-b0f79ee/src/atlas/grid/CubedSphereGrid.h0000664000175000017500000003343615263665111022613 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 ecmwf-atlas-b0f79ee/src/atlas/grid/Distribution.cc0000664000175000017500000000375615263665111022433 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/StructuredGrid.h0000664000175000017500000002354315263665111022564 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/Distribution.h0000664000175000017500000000502215263665111022261 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/StencilComputer.cc0000664000175000017500000001025515263665111023064 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/Tiles.cc0000664000175000017500000000512515263665111021024 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 ecmwf-atlas-b0f79ee/src/atlas/grid/StructuredPartitionPolygon.cc0000664000175000017500000004173115263665111025355 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/grid/Iterator.h0000664000175000017500000001337315263665111021403 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/UnstructuredGrid.h0000664000175000017500000000500315263665111023116 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/SpecRegistry.cc0000664000175000017500000000154215263665111022366 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/grid/Partitioner.cc0000664000175000017500000001545715263665111022255 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/functionspace/detail/FunctionSpaceImpl.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/mesh/detail/MeshImpl.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; } detail::partitioner::Partitioner* atlas__grid__MatchingPartitioner__new(const util::Object* object, const Partitioner::Config* config) { if (const auto* mesh = dynamic_cast(object)) { return atlas__grid__MatchingMeshPartitioner__new(mesh, config); } if (const auto* functionspace = dynamic_cast(object)) { return atlas__grid__MatchingFunctionSpacePartitioner__new(functionspace, config); } ATLAS_THROW_EXCEPTION("MatchingPartitioner requires a Mesh or FunctionSpace"); } 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 ecmwf-atlas-b0f79ee/src/atlas/grid/StencilComputer.h0000664000175000017500000001440015263665111022722 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/grid/GridPointFlags.h0000664000175000017500000000431215263665111022457 0ustar alastairalastair/* * (C) Copyright 2026- ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 GridPointFlags { public: using Representation = int; enum Bit : Representation { none = 0, duplicate = 1 << 0, extension = 1 << 1, invalid = 1 << 2 }; constexpr GridPointFlags() = default; constexpr GridPointFlags(Bit flag): value_(flag) {} explicit constexpr GridPointFlags(Representation value): value_(value) {} constexpr Representation value() const { return value_; } constexpr bool has(GridPointFlags flags) const { return (value_ & flags.value_) == flags.value_; } constexpr GridPointFlags& set(GridPointFlags flags) { value_ |= flags.value_; return *this; } constexpr GridPointFlags& unset(GridPointFlags flags) { value_ &= ~flags.value_; return *this; } constexpr GridPointFlags& operator|=(GridPointFlags flags) { return set(flags); } friend constexpr GridPointFlags operator|(GridPointFlags lhs, GridPointFlags rhs) { return lhs.set(rhs); } friend constexpr bool operator==(GridPointFlags lhs, GridPointFlags rhs) { return lhs.value_ == rhs.value_; } friend constexpr bool operator!=(GridPointFlags lhs, GridPointFlags rhs) { return !(lhs == rhs); } private: Representation value_{0}; }; static_assert(std::is_standard_layout::value, "GridPointFlags must have a stable scalar layout"); static_assert(std::is_trivially_copyable::value, "GridPointFlags must be transferable as bytes"); static_assert(sizeof(GridPointFlags) == sizeof(GridPointFlags::Representation), "GridPointFlags must have the same representation as int"); static_assert(alignof(GridPointFlags) == alignof(GridPointFlags::Representation), "GridPointFlags must have the same alignment as int"); } // namespace atlasecmwf-atlas-b0f79ee/src/atlas/grid/Tiles.h0000664000175000017500000000637215263665111020673 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 ecmwf-atlas-b0f79ee/src/atlas/grid/Grid.cc0000664000175000017500000000541315263665111020631 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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(); } int Grid::flags(gidx_t index) const { return get()->flags(index); } gidx_t Grid::masterIndex(gidx_t index) const { return get()->masterIndex(index); } 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/0000775000175000017500000000000015263665111020301 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/parallel/GatherScatter.h0000664000175000017500000006737115263665111023230 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 "pluto/pluto.h" #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 with explicit control over sparse global-index mapping /// @param [in] preserve_global_indices Keep global indices as field positions when true; /// sort, deduplicate, and compact sparse indices when false 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, bool preserve_global_indices); /// @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 validate_field_and_map(const char* context, const FIELD& field, const std::vector& map, idx_t field_point_count, idx_t buffer_point_count, size_t buffer_size) const; template void pack_send_buffer(const parallel::Field& field, const std::vector& sendmap, DATA_TYPE send_buffer[], size_t send_buffer_size) const; template void unpack_recv_buffer(const std::vector& recvmap, const DATA_TYPE recv_buffer[], size_t recv_buffer_size, 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_; idx_t global_field_dof_; 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()); } std::vector glb_displs(nproc); std::vector glb_counts(nproc); 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); for (idx_t jproc = 0; jproc < nproc; ++jproc) { glb_counts[jproc] = glbcounts_[jproc] * gvar_size; glb_displs[jproc] = glbdispls_[jproc] * gvar_size; } #if !defined(NDEBUG) validate_field_and_map("GatherScatter::gather local field", lfields[jfield], locmap_, parsize_, loccnt_, loc_size); if (myproc == root) { validate_field_and_map("GatherScatter::gather global field", gfields[jfield], glbmap_, global_field_dof_, glb_cnt(root), glb_size); } #endif /// Pack pack_send_buffer(lfields[jfield], locmap_, loc_buffer.data(), loc_size); /// Gather ATLAS_TRACE_MPI(GATHER) { comm().gatherv(loc_buffer.begin(), loc_buffer.end(), glb_buffer.begin(), glb_buffer.end(), glb_counts, glb_displs, root); } /// Unpack if (myproc == root) unpack_recv_buffer(glbmap_, glb_buffer.data(), glb_size, 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 { ATLAS_TRACE("GatherScatter::scatter"); if (!is_setup_) { throw_Exception("GatherScatter was not setup", Here()); } std::vector glb_displs(nproc); std::vector glb_counts(nproc); 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); for (idx_t jproc = 0; jproc < nproc; ++jproc) { glb_counts[jproc] = glbcounts_[jproc] * gvar_size; glb_displs[jproc] = glbdispls_[jproc] * gvar_size; } #if !defined(NDEBUG) validate_field_and_map("GatherScatter::scatter local field", lfields[jfield], locmap_, parsize_, loccnt_, loc_size); if (myproc == root) { validate_field_and_map("GatherScatter::scatter global field", gfields[jfield], glbmap_, global_field_dof_, glb_cnt(root), glb_size); } #endif /// Pack if (myproc == root) { pack_send_buffer(gfields[jfield], glbmap_, glb_buffer.data(), glb_size); } /// 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(), loc_size, 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::validate_field_and_map(const char* context, const FIELD& field, const std::vector& map, idx_t field_point_count, idx_t buffer_point_count, size_t buffer_size) const { auto format_idx_vector = [](const auto& values) { std::ostringstream out; out << '['; for (size_t idx = 0; idx < values.size(); ++idx) { if (idx > 0) { out << ','; } out << values[idx]; } out << ']'; return out.str(); }; auto base_message = [&]() { std::ostringstream out; out << context << ": " << "var_rank=" << field.var_rank << ", var_shape=" << format_idx_vector(field.var_shape) << ", var_strides=" << format_idx_vector(field.var_strides) << ", map_size=" << map.size() << ", field_point_count=" << field_point_count << ", buffer_point_count=" << buffer_point_count << ", buffer_size=" << buffer_size << ", data=" << static_cast(field.data); return out.str(); }; if (field.var_rank < 1 || field.var_rank > 3) { throw_Exception(base_message() + ", unsupported field rank", Here()); } if (field.var_shape.size() != static_cast(field.var_rank) || field.var_strides.size() != static_cast(field.var_rank)) { throw_Exception(base_message() + ", invalid field metadata", Here()); } idx_t per_point_size = 1; for (idx_t dim = 0; dim < field.var_rank; ++dim) { if (field.var_shape[dim] < 0) { std::ostringstream out; out << base_message() << ", invalid field shape at dim=" << dim << ", value=" << field.var_shape[dim]; throw_Exception(out.str(), Here()); } per_point_size *= field.var_shape[dim]; } const size_t expected_buffer_size = static_cast(buffer_point_count * per_point_size); if (expected_buffer_size != buffer_size) { std::ostringstream out; out << base_message() << ", inconsistent buffer size, expected_buffer_size=" << expected_buffer_size << ", per_point_size=" << per_point_size; throw_Exception(out.str(), Here()); } if (buffer_size > 0 && field.data == nullptr) { throw_Exception(base_message() + ", field data pointer is null", Here()); } if (buffer_size > 0 && per_point_size > 0 && field_point_count == 0) { std::ostringstream out; out << base_message() << ", non-empty buffer with zero field extent, per_point_size=" << per_point_size; throw_Exception(out.str(), Here()); } for (size_t map_idx = 0; map_idx < map.size(); ++map_idx) { const int mapped_point = map[map_idx]; if (mapped_point < 0 || static_cast(mapped_point) >= field_point_count) { std::ostringstream out; out << base_message() << ", map index out of range at map_idx=" << map_idx << ", mapped_point=" << mapped_point; throw_Exception(out.str(), Here()); } } } template void GatherScatter::pack_send_buffer(const parallel::Field& field, const std::vector& sendmap, DATA_TYPE send_buffer[], size_t send_buffer_size) const { const idx_t sendcnt = static_cast(sendmap.size()); const idx_t send_buffer_elements = std::accumulate(field.var_shape.begin(), field.var_shape.end(), idx_t{1}, std::multiplies()) * sendcnt; ATLAS_ASSERT(static_cast(send_buffer_elements) == send_buffer_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; } ATLAS_ASSERT(static_cast(ibuf) == send_buffer_size); } template void GatherScatter::unpack_recv_buffer(const std::vector& recvmap, const DATA_TYPE recv_buffer[], size_t recv_buffer_size, const parallel::Field& field) const { const idx_t recvcnt = static_cast(recvmap.size()); const idx_t recv_buffer_elements = std::accumulate(field.var_shape.begin(), field.var_shape.end(), idx_t{1}, std::multiplies()) * recvcnt; ATLAS_ASSERT(static_cast(recv_buffer_elements) == recv_buffer_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; } ATLAS_ASSERT(ibuf == recv_buffer_size); } 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/acc/0000775000175000017500000000000015263665111021027 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/parallel/acc/acc.h0000664000175000017500000000164115263665111021730 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/acc/acc.cc0000664000175000017500000000364215263665111022071 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 } } ecmwf-atlas-b0f79ee/src/atlas/parallel/detail/0000775000175000017500000000000015263665111021543 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/parallel/detail/zero_index.h0000664000175000017500000000437715263665111024075 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/detail/DevicePacker.hic0000664000175000017500000002265515263665111024567 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/detail/pack_index.h0000664000175000017500000001117115263665111024022 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/detail/Packer.h0000664000175000017500000001176115263665111023127 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/detail/adjoint_unpack_index.h0000664000175000017500000000462415263665111026102 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/detail/Packer.cc0000664000175000017500000001107615263665111023264 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/HaloExchange.h0000664000175000017500000004707515263665111023015 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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); field.setDeviceNeedsUpdate(!on_device); field.setHostNeedsUpdate(on_device); 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); field.setDeviceNeedsUpdate(!on_device); field.setHostNeedsUpdate(on_device); 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/HaloExchange.cc0000664000175000017500000002467415263665111023153 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/omp/0000775000175000017500000000000015263665111021074 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/parallel/omp/copy.h0000664000175000017500000000260015263665111022215 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/omp/omp.h0000664000175000017500000000440615263665111022044 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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) #define atlas_omp_atomic atlas_omp_pragma(omp atomic) #define atlas_omp_atomic_update atlas_omp_pragma(omp atomic update) #define atlas_omp_atomic_write atlas_omp_pragma(omp atomic write) #define atlas_omp_atomic_read atlas_omp_pragma(omp atomic read) #define atlas_omp_atomic_capture atlas_omp_pragma(omp atomic capture) #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 ecmwf-atlas-b0f79ee/src/atlas/parallel/omp/omp.cc0000664000175000017500000000572215263665111022204 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 } ecmwf-atlas-b0f79ee/src/atlas/parallel/omp/fill.h0000664000175000017500000000225015263665111022172 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/omp/sort.h0000664000175000017500000002146615263665111022245 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/Collect.h0000664000175000017500000002112715263665111022042 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/GatherScatter.cc0000664000175000017500000004403215263665111023353 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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_(), global_field_dof_(0), is_setup_(false) { } GatherScatter::GatherScatter(const std::string& name): name_(name), global_field_dof_(0), 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, mask, 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) { setup(mpi_comm, part, remote_idx, remote_idx_base, glb_idx, mask, parsize, false); } 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, bool preserve_global_indices) { 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 nodes; try { nodes.resize(nb_recv_nodes); } catch (const std::bad_alloc& e) { ATLAS_THROW_EXCEPTION("Could not allocate nodes 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) { nodes[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) { nodes[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) { nodes[n].i = recvnodes_ridx[n]; } sendnodes_ridx.clear(); } auto can_use_global_indices_directly = [&]() { if (preserve_global_indices) { return true; } if (nb_recv_nodes == 0) { return true; } std::vector dense_global_index_seen(nb_recv_nodes, false); for (const auto& node : nodes) { const gidx_t dense_index = node.g - glb_idx_base; if (dense_index < 0 || dense_index >= nb_recv_nodes || dense_global_index_seen[dense_index]) { return false; } dense_global_index_seen[dense_index] = true; } return true; }; auto sort_and_unique_sparse_global_indices = [&]() { // Sort on "g" member, and remove duplicates ATLAS_TRACE_SCOPE("sorting") { // omp::sort(nodes.begin(), nodes.end()); std::sort(nodes.begin(), nodes.end()); } ATLAS_TRACE_SCOPE("remove duplicates") { nodes.erase(std::unique(nodes.begin(), nodes.end()), nodes.end()); } }; const bool use_global_indices_directly = can_use_global_indices_directly(); if (!use_global_indices_directly) { sort_and_unique_sparse_global_indices(); } glbcounts_.assign(nproc, 0); glbdispls_.assign(nproc, 0); for (size_t n = 0; n < nodes.size(); ++n) { ++glbcounts_[nodes[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); auto update_local_map = [&](const Node& node) { if (node.p == myproc) { locmap_[idx[node.p]] = node.i; } }; if (use_global_indices_directly) { for (const auto& node : nodes) { glbmap_[glbdispls_[node.p] + idx[node.p]] = node.g - glb_idx_base; update_local_map(node); ++idx[node.p]; } } else { gidx_t compact_global_index{0}; for (const auto& node : nodes) { glbmap_[glbdispls_[node.p] + idx[node.p]] = compact_global_index++; update_local_map(node); ++idx[node.p]; } } global_field_dof_ = glbmap_.empty() ? 0 : *std::max_element(glbmap_.begin(), glbmap_.end()) + 1; 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/mpi/0000775000175000017500000000000015263665111021066 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/parallel/mpi/Buffer.h0000664000175000017500000000412415263665111022451 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/mpi/mpi.cc0000664000175000017500000001420615263665111022165 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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::detail::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 ecmwf-atlas-b0f79ee/src/atlas/parallel/mpi/mpi.h0000664000175000017500000000775315263665111022040 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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/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 { namespace detail { void push(const Comm& comm); } /// @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() template , Comm>, int> = 0> void push(CommT&& comm) { detail::push(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); } template , Comm>, int> = 0> Scope(CommT&& 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/mpi/Statistics.h0000664000175000017500000000426415263665111023377 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/Locate.cc0000664000175000017500000001415615263665111022026 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/Checksum.h0000664000175000017500000002010615263665111022213 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/Locate.h0000664000175000017500000001116515263665111021665 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/Collect.cc0000664000175000017500000001235515263665111022203 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 ecmwf-atlas-b0f79ee/src/atlas/parallel/Checksum.cc0000664000175000017500000001111415263665111022350 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/0000775000175000017500000000000015263665111021346 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/functionspace/BlockStructuredColumns.cc0000664000175000017500000000566315263665111026347 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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())) {} // ---------------------------------------------------------------------------- } // namespace functionspace } // namespace atlas ecmwf-atlas-b0f79ee/src/atlas/functionspace/Spectral.h0000664000175000017500000003021415263665111023274 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 index = 0; for( idx_t jm=0; jm n ) { continue; } const idx_t offset_for_zonal_wavenumber = offsets_by_zonal_wavenumber[m]; const idx_t index = offset_for_zonal_wavenumber + 2 * (n - m); data( index, level ) = func_real_part(n,m,level); data( index+1, level ) = func_imag_part(n,m,level); } } The same storage order can also be accessed with parallel_for(). spectral.parallel_for([&]( idx_t real, idx_t imag, int n, int m ) { data( real, level ) = func_real_part(n,m,level); data( imag, level ) = func_imag_part(n,m,level); }); */ public: Spectral(const eckit::Configuration&); Spectral(const int truncation, const eckit::Configuration& = util::NoConfig()); Spectral(const long truncation, const eckit::Configuration& = util::NoConfig()); Spectral(const size_t truncation, const eckit::Configuration& = util::NoConfig()); explicit Spectral(bool) = delete; ~Spectral() override; std::string type() const override { return "Spectral"; } std::string distribution() const override; std::string mpi_comm() const override { return mpi_comm_; } idx_t part() const override; idx_t nb_parts() const override; using FunctionSpaceImpl::createField; Field createField(const eckit::Configuration&) const override; Field createField(const Field&, const eckit::Configuration&) const override; using FunctionSpaceImpl::gather; void gather(const FieldSet&, FieldSet&) const override; void gather(const Field&, Field&) const override; using FunctionSpaceImpl::scatter; void scatter(const FieldSet&, FieldSet&) const override; void scatter(const Field&, Field&) const override; std::string checksum(const FieldSet&) const override; std::string checksum(const Field&) const override; 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 array::LocalView offsets_by_zonal_wavenumber() const; // zero-based nasm0 for use in C++ 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()}, offsets_by_zonal_wavenumber{fs.offsets_by_zonal_wavenumber()}, global{config.getBool("global", false)}, owner{config.getInt("owner", 0)}, mpi_comm{fs.mpi_comm()} {} protected: using View = const array::LocalView; int truncation; View zonal_wavenumbers; View offsets_by_zonal_wavenumber; bool global; idx_t owner; std::string mpi_comm; 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 { if (global) { const auto& comm = mpi::comm(mpi_comm); if (comm.size() == 0 || owner == comm.rank()) { atlas_omp_parallel_for(int m = 0; m <= truncation; ++m) { idx_t index = global_offset_by_zonal_wavenumber(m); for (int n = m; n <= truncation; ++n, index += 2) { f(index, index + 1, n, m); } } } } else { const int nb_zonal_wavenumbers{static_cast(zonal_wavenumbers.size())}; atlas_omp_parallel_for(int jm = 0; jm < nb_zonal_wavenumbers; ++jm) { const int m = zonal_wavenumbers(jm); idx_t index = offsets_by_zonal_wavenumber[m]; for (int n = m; n <= truncation; ++n, index += 2) { f(index, index + 1, n, m); } } } } // Functor: void f(real,imag,n) template void operator()(const Functor& f) const { if (global) { const auto& comm = mpi::comm(mpi_comm); if (comm.size() == 0 || owner == comm.rank()) { atlas_omp_parallel_for(int m = 0; m <= truncation; ++m) { idx_t index = global_offset_by_zonal_wavenumber(m); for (int n = m; n <= truncation; ++n, index += 2) { f(index, index + 1, n); } } } } else { const int nb_zonal_wavenumbers{static_cast(zonal_wavenumbers.size())}; atlas_omp_parallel_for(int jm = 0; jm < nb_zonal_wavenumbers; ++jm) { const int m = zonal_wavenumbers(jm); idx_t index = offsets_by_zonal_wavenumber[m]; for (int n = m; n <= truncation; ++n, index += 2) { f(index, index + 1, n); } } } } idx_t global_offset_by_zonal_wavenumber(const int m) const { // For the global packed triangular layout: // idx_t index = 0; // for(int m = 0; m <= truncation; ++m) { // for (int n = m; n <= truncation; ++n) { // f(index, index + 1, n); // index += 2; // } // } // The value `index` can be computed directly rather than accumulated: // index = offset(m) + 2 * (n - m) // where // offset(m) = sum_{k=0}^{m-1} 2 * (truncation - k + 1) // which simplifies to: // offset(m) = m * (2 * truncation + 3 - m) return static_cast(m) * (static_cast(truncation) * 2 + 3 - m); } #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; // Number of zonal wave numbers m on THIS rank, equivalent to nmyms().size() array::LocalView nvalue() const; // Return wave number n for a given index array::LocalView nmyms() const; // Array of actual m values (zonal wave numbers) on this rank (size nump) array::LocalView nasm0_base0() const; // Base offset in memory for this zonal wave number array::LocalView nasm0_base1() const; // Base offset in memory for this zonal wave number private: // data std::string mpi_comm_; idx_t nb_levels_; int truncation_; class Parallelisation; friend class Parallelisation_ectrans; friend class Parallelisation_local; 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()); Spectral(const long truncation, const eckit::Configuration& = util::NoConfig()); Spectral(const size_t truncation, const eckit::Configuration& = util::NoConfig()); explicit Spectral(bool) = delete; 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 array::LocalView offsets_by_zonal_wavenumber() const; // zero-based nasm0 for use in C++ 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/CubedSphereColumns.h0000664000175000017500000001334515263665111025257 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/EdgeColumns.cc0000664000175000017500000006550515263665111024075 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/detail/ColumnChecksum.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::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 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, mpi_comm()); 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); } std::string EdgeColumns::checksum(const FieldSet& fieldset) const { return ColumnChecksum(*this, static_cast(nb_edges_global())).compute(fieldset); } std::string EdgeColumns::checksum(const Field& field) const { return ColumnChecksum(*this, static_cast(nb_edges_global())).compute(field); } const parallel::Checksum& EdgeColumns::checksum() const { return deprecated_checksum(); } const parallel::Checksum& EdgeColumns::deprecated_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::ghost() const { if (mesh_.edges().has_field("ghost")) { return mesh_.edges().field("ghost"); } return mesh_.edges().halo(); } 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->deprecated_checksum(); } // ----------------------------------------------------------------------------------- } // ----------------------------------------------------------------------------------- } // 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(); } const parallel::Checksum& EdgeColumns::checksum() const { return functionspace_->deprecated_checksum(); } } // namespace functionspace } // namespace atlas ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/0000775000175000017500000000000015263665111022610 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/BlockStructuredColumns.cc0000664000175000017500000005753615263665111027617 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 #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 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 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 // ---------------------------------------------------------------------------- namespace { struct ChecksumData { Field local_lane_checksums; Field global_lane_checksums; int root{0}; }; bool block_slice_contiguous(const Field& f) { idx_t expected_stride = 1; for (idx_t dim = f.rank() - 1; dim > 0; --dim) { if (f.stride(dim) != expected_stride) { return false; } expected_stride *= f.shape(dim); } return true; } template void checksum_update_lane(util::checksum_t& checksum, const array::Array& f, const T* data, idx_t dim, idx_t offset) { if (dim == f.rank() - 1) { util::checksum_update(checksum, data + offset, 1); return; } for (idx_t index = 0; index < f.shape(dim); ++index) { checksum_update_lane(checksum, f, data, dim + 1, offset + index * f.stride(dim)); } } template void update_lane_checksums_with_field_contiguous(const BlockStructuredColumns& fs, const Field& f, const T* data, array::ArrayView& local_lane_checksums_view) { const idx_t nproma = f.shape(f.rank() - 1); const idx_t full_blocks = (fs.nblks() > 0 ? fs.nblks() - 1 : 0); if (f.rank() == 4) { auto update_block = [&](idx_t jblk, idx_t block_size) { const idx_t block_offset = jblk * f.stride(0); const T* block_data = data + block_offset; // [nblks, nvar, nlev, nproma]: iterate lev-outer, var-inner // to match global field order [npoints, nlev, nvar] const idx_t nvar = f.shape(1); const idx_t nlev = f.shape(2); const size_t lane_size = static_cast(nvar); const size_t lane_stride = static_cast(nlev * nproma); for (idx_t jlev = 0; jlev < nlev; ++jlev) { for (idx_t jlane = 0; jlane < block_size; ++jlane) { const T* lane_data = block_data + jlev * nproma + jlane; util::checksum_update(local_lane_checksums_view(jblk, jlane), lane_data, lane_size, lane_stride); } } }; atlas_omp_parallel_for(idx_t jblk = 0; jblk < full_blocks; ++jblk) { update_block(jblk, nproma); } if (fs.nblks() > 0) { update_block(fs.nblks() - 1, fs.block_size(fs.nblks() - 1)); } } else { const size_t lane_stride = static_cast(nproma); size_t lane_size{1}; for (idx_t dim = 1; dim < f.rank() - 1; ++dim) { lane_size *= f.shape(dim); } auto update_block = [&](idx_t jblk, idx_t block_size) { const idx_t block_offset = jblk * f.stride(0); const T* block_data = data + block_offset; for (idx_t jlane = 0; jlane < block_size; ++jlane) { const T* lane_data = block_data + jlane; util::checksum_update(local_lane_checksums_view(jblk, jlane), lane_data, lane_size, lane_stride); } }; atlas_omp_parallel_for(idx_t jblk = 0; jblk < full_blocks; ++jblk) { update_block(jblk, nproma); } if (fs.nblks() > 0) { update_block(fs.nblks() - 1, fs.block_size(fs.nblks() - 1)); } } } template void update_lane_checksums_with_field(const BlockStructuredColumns& fs, const Field& f, ChecksumData& checksum_data) { auto local_lane_checksums_view = array::make_view(checksum_data.local_lane_checksums); const auto* data = f.array().host_data(); if (block_slice_contiguous(f)) { update_lane_checksums_with_field_contiguous(fs, f, data, local_lane_checksums_view); return; } // A fallback; we should usually not be here normally, as a field block is usually contiguous const idx_t lane_stride = f.stride(f.rank() - 1); atlas_omp_parallel_for (idx_t jblk = 0; jblk < fs.nblks(); ++jblk) { const idx_t block_offset = jblk * f.stride(0); const idx_t block_size = fs.block_size(jblk); if (f.rank() == 4) { // Iterate lev-outer, var-inner to match global field order [npoints, nlev, nvar] const idx_t nvar = f.shape(1); const idx_t nlev = f.shape(2); for (idx_t jlane = 0; jlane < block_size; ++jlane) { util::checksum_t lane_checksum = local_lane_checksums_view(jblk, jlane); for (idx_t jlev = 0; jlev < nlev; ++jlev) { for (idx_t jvar = 0; jvar < nvar; ++jvar) { const idx_t offset = block_offset + jlane * lane_stride + jvar * f.stride(1) + jlev * f.stride(2); util::checksum_update(lane_checksum, data + offset, size_t{1}); } } local_lane_checksums_view(jblk, jlane) = lane_checksum; } } else { for (idx_t jlane = 0; jlane < block_size; ++jlane) { util::checksum_t lane_checksum = local_lane_checksums_view(jblk, jlane); checksum_update_lane(lane_checksum, f, data, idx_t{1}, block_offset + jlane * lane_stride); local_lane_checksums_view(jblk, jlane) = lane_checksum; } } } } void update_lane_checksums_with_field_dynamic(const BlockStructuredColumns& fs, const Field& f, ChecksumData& checksum_data) { switch (f.datatype().kind()) { case array::DataType::kind(): return update_lane_checksums_with_field(fs, f, checksum_data); case array::DataType::kind(): return update_lane_checksums_with_field(fs, f, checksum_data); case array::DataType::kind(): return update_lane_checksums_with_field(fs, f, checksum_data); case array::DataType::kind(): return update_lane_checksums_with_field(fs, f, checksum_data); default: throw_Exception("datatype not supported", Here()); } } ChecksumData get_checksum_data(const BlockStructuredColumns& fs) { pluto::scope scope; pluto::host::set_default_resource(pluto::host_pool_resource()); ChecksumData checksum_data; checksum_data.root = 0; checksum_data.local_lane_checksums = fs.createField(option::name("checksum_local_lane_checksums") | option::datatypeT() | option::levels(0)); checksum_data.global_lane_checksums = fs.createField(checksum_data.local_lane_checksums, option::global(checksum_data.root)); auto local_lane_checksums_view = array::make_view(checksum_data.local_lane_checksums); local_lane_checksums_view.assign(0); return checksum_data; } util::checksum_t checksum_from_lane_states(const BlockStructuredColumns& fs, ChecksumData& checksum_data) { auto& local_lane_checksums = checksum_data.local_lane_checksums; auto local_lane_checksums_view = array::make_view(local_lane_checksums); for (idx_t jblk = 0; jblk < fs.nblks(); ++jblk) { for (idx_t jlane = 0; jlane < fs.nproma(); ++jlane) { local_lane_checksums_view(jblk, jlane) = util::checksum_digest(local_lane_checksums_view(jblk, jlane)); } } auto& mpi_comm = mpi::comm(fs.mpi_comm()); if (mpi_comm.size() == 1) { ATLAS_ASSERT(local_lane_checksums.contiguous()); const auto* local_lane_checksums_data = local_lane_checksums.array().host_data(); return util::checksum(local_lane_checksums_data, static_cast(fs.structuredcolumns().sizeOwned())); } auto& global_lane_checksums = checksum_data.global_lane_checksums; fs.gather(local_lane_checksums, global_lane_checksums); util::checksum_t global_checksum = 0; if (mpi_comm.rank() == checksum_data.root) { ATLAS_ASSERT(global_lane_checksums.contiguous()); const auto* global_lane_checksums_data = global_lane_checksums.array().host_data(); global_checksum = util::checksum(global_lane_checksums_data, global_lane_checksums.size()); } mpi_comm.broadcast(global_checksum, checksum_data.root); return global_checksum; } util::checksum_t field_checksum(const BlockStructuredColumns& fs, const Field& f) { ATLAS_ASSERT(f.rank() > 1); auto checksum_data = get_checksum_data(fs); update_lane_checksums_with_field_dynamic(fs, f, checksum_data); return checksum_from_lane_states(fs, checksum_data); } util::checksum_t fieldset_checksum(const BlockStructuredColumns& fs, const FieldSet& fields) { auto checksum_data = get_checksum_data(fs); for (idx_t jfld = 0; jfld < fields.size(); ++jfld) { update_lane_checksums_with_field_dynamic(fs, fields[jfld], checksum_data); } return checksum_from_lane_states(fs, checksum_data); } } // namespace std::string BlockStructuredColumns::checksum(const Field& f) const { ATLAS_TRACE("BlockStructuredColumns::checksum"); return util::checksum_to_hex_str(field_checksum(*this, f)); } std::string BlockStructuredColumns::checksum(const FieldSet& f) const { ATLAS_TRACE("BlockStructuredColumns::checksum"); return util::checksum_to_hex_str(fieldset_checksum(*this, f)); } // ---------------------------------------------------------------------------- } // namespace detail } // namespace functionspace } // namespace atlas ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/SpectralInterface.cc0000664000175000017500000001223615263665111026521 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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_base0() const { return fs_.nasm0_base0(); } array::LocalView nasm0_base1() const { return fs_.nasm0_base1(); } }; } // 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_base1(const detail::Spectral* This, const int*& nasm0, int& size) { const auto nasm0_ = SpectralFortranAccess(*This).nasm0_base1(); 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/FunctionSpaceInterface.h0000664000175000017500000000552715263665111027354 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the 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); field::FieldImpl* atlas__FunctionSpace__lonlat(const FunctionSpaceImpl* This); field::FieldImpl* atlas__FunctionSpace__ghost(const FunctionSpaceImpl* This); field::FieldImpl* atlas__FunctionSpace__global_index(const FunctionSpaceImpl* This); field::FieldImpl* atlas__FunctionSpace__remote_index(const FunctionSpaceImpl* This); field::FieldImpl* atlas__FunctionSpace__partition(const FunctionSpaceImpl* This); 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); void atlas__FunctionSpace__checksum_field(const FunctionSpaceImpl* This, const field::FieldImpl* field, char*& checksum, int& size); void atlas__FunctionSpace__checksum_fieldset(const FunctionSpaceImpl* This, const field::FieldSetImpl* fieldset, char*& checksum, int& size); } //------------------------------------------------------------------------------------------------------ } // namespace functionspace //------------------------------------------------------------------------------------------------------ } // namespace atlas ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/StructuredColumnsInterface.cc0000664000175000017500000002066115263665111030452 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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__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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/FunctionSpaceImpl.h0000664000175000017500000001225415263665111026350 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 std::string checksum(const FieldSet&) const; virtual std::string checksum(const 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 atlas::Field mask() const; virtual bool hasMask() const; virtual void setMask(const atlas::Field& global_mask); 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/BlockStructuredColumnsInterface.h0000664000175000017500000001411215263665111031261 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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__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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/CubedSphereStructure.h0000664000175000017500000000464515263665111027104 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/PointCloudInterface.cc0000664000175000017500000000364415263665111027027 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/BlockStructuredColumns.h0000664000175000017500000001343415263665111027446 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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(); } const Projection& projection() const override { return grid().projection(); } 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 override; std::string checksum(const Field&) const override; const util::PartitionPolygon& polygon(idx_t halo = 0) const override { return structuredcolumns_->polygon(halo); } std::string mpi_comm() const override { return structuredcolumns_->mpi_comm(); } 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/StructuredColumns_setup.cc0000664000175000017500000006434415263665111030057 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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(); grid_is_regional_ = regional; north_pole_included_ = (!regional && grid_->projection().units() == "degrees" && 90. - grid_->y(0) == 0.); south_pole_included_ = (!regional && grid_->projection().units() == "degrees" && 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/FunctionSpaceImpl.cc0000664000175000017500000001525715263665111026514 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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()) { metadata().set("has_mask", false); } 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 { throw_Exception("haloExchange() not implemented in derived class ["+type()+"]", Here()); } void FunctionSpaceImpl::haloExchange(const Field&, bool) const { throw_Exception("haloExchange() not implemented in derived class ["+type()+"]", Here()); } void FunctionSpaceImpl::adjointHaloExchange(const FieldSet&, bool) const { throw_Exception("adjointHaloExchange() not implemented in derived class ["+type()+"]", Here()); } void FunctionSpaceImpl::adjointHaloExchange(const Field&, bool) const { throw_Exception("adjointHaloExchange() not implemented in derived class ["+type()+"]", Here()); } Field NoFunctionSpace::createField(const eckit::Configuration&) const { throw_Exception("createField(const eckit::Configuration&) not implemented in derived class ["+type()+"]", Here()); } Field NoFunctionSpace::createField(const Field&, const eckit::Configuration&) const { throw_Exception("createField(const Field&, const eckit::Configuration&) not implemented in derived class ["+type()+"]", Here()); } const Grid& FunctionSpaceImpl::grid() const { throw_Exception("grid() not implemented in derived class ["+type()+"]", Here()); } Field FunctionSpaceImpl::lonlat() const { throw_Exception("lonlat() not implemented in derived class ["+type()+"]", Here()); } Field FunctionSpaceImpl::ghost() const { throw_Exception("ghost() not implemented in derived class ["+type()+"]", Here()); } Field FunctionSpaceImpl::remote_index() const { throw_Exception("remote_index() not implemented in derived class ["+type()+"]", Here()); } Field FunctionSpaceImpl::partition() const { throw_Exception("partition() not implemented in derived class ["+type()+"]", Here()); } Field FunctionSpaceImpl::global_index() const { throw_Exception("global_index() not implemented in derived class ["+type()+"]", Here()); } Field FunctionSpaceImpl::mask() const { ATLAS_NOTIMPLEMENTED; } bool FunctionSpaceImpl::hasMask() const { return metadata().getBool("has_mask"); } void FunctionSpaceImpl::setMask(const Field& global_mask) { Field local_mask = mask(); scatter(global_mask, local_mask); haloExchange(local_mask); metadata().set("has_mask", true); } const util::PartitionPolygon& FunctionSpaceImpl::polygon(idx_t /*halo */) const { throw_Exception("polygon() not implemented in derived class ["+type()+"]", Here()); } const util::PartitionPolygons& FunctionSpaceImpl::polygons() const { throw_Exception("polygons() not implemented in derived class ["+type()+"]", Here()); } const Projection& FunctionSpaceImpl::projection() const { throw_Exception("projection() not implemented in derived class ["+type()+"]", 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 { throw_Exception("part() not implemented in derived class ["+type()+"]", Here()); } idx_t FunctionSpaceImpl::nb_parts() const { throw_Exception("nb_parts() not implemented in derived class ["+type()+"]", Here()); } void FunctionSpaceImpl::gather(const FieldSet& local, FieldSet& global) const { throw_Exception("gather() not implemented in derived class ["+type()+"]", Here()); } void FunctionSpaceImpl::gather(const Field& local, Field& global) const { throw_Exception("gather() not implemented in derived class ["+type()+"]", Here()); } void FunctionSpaceImpl::scatter(const FieldSet& global, FieldSet& local) const { throw_Exception("scatter() not implemented in derived class ["+type()+"]", Here()); } void FunctionSpaceImpl::scatter(const Field& global, Field& local) const { throw_Exception("scatter() not implemented in derived class ["+type()+"]", Here()); } std::string FunctionSpaceImpl::checksum(const FieldSet&) const { throw_Exception("checksum() not implemented in derived class ["+type()+"]", Here()); } std::string FunctionSpaceImpl::checksum(const Field&) const { throw_Exception("checksum() not implemented in derived class ["+type()+"]", Here()); } const parallel::GatherScatter& FunctionSpaceImpl::gather() const { throw_Exception("gather() not implemented in derived class ["+type()+"]", Here()); } const parallel::GatherScatter& FunctionSpaceImpl::scatter() const { throw_Exception("scatter() not implemented in derived class ["+type()+"]", Here()); } 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/PointCloudInterface.h0000664000175000017500000000356315263665111026671 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/NodeColumns_FieldStatistics.cc0000664000175000017500000017457015263665111030541 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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); const mesh::IsGhostNode is_ghost(fs.nodes()); 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; if (!is_ghost(n)) { 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 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); 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) { if (!is_ghost(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 mesh::IsGhostNode is_ghost(fs.nodes()); 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) { if (!is_ghost(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 mesh::IsGhostNode is_ghost(fs.nodes()); 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) { if (!is_ghost(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); const mesh::IsGhostNode is_ghost(fs.nodes()); 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 = std::min(arr.shape(0), fs.nb_nodes()); atlas_omp_for(idx_t n = 0; n < npts; ++n) { if (!is_ghost(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); const mesh::IsGhostNode is_ghost(fs.nodes()); 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 = std::min(arr.shape(0), fs.nb_nodes()); atlas_omp_for(idx_t n = 0; n < npts; ++n) { if (!is_ghost(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); const mesh::IsGhostNode is_ghost(fs.nodes()); 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 = std::min(arr.shape(0), fs.nb_nodes()); atlas_omp_for(idx_t n = 0; n < npts; ++n) { if (!is_ghost(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); const mesh::IsGhostNode is_ghost(fs.nodes()); 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 = std::min(arr.shape(0), fs.nb_nodes()); atlas_omp_for(idx_t n = 0; n < npts; ++n) { if (!is_ghost(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); const 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)); } 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 = std::min(arr.shape(0), fs.nb_nodes()); atlas_omp_for(idx_t n = 0; n < npts; ++n) { if (!is_ghost(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_private_view(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); const 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)); } 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 = std::min(arr.shape(0), fs.nb_nodes()); atlas_omp_for(idx_t n = 0; n < npts; ++n) { if (!is_ghost(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_private_view(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); const mesh::IsGhostNode is_ghost(fs.nodes()); 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) { if (!is_ghost(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); const mesh::IsGhostNode is_ghost(fs.nodes()); 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) { if (!is_ghost(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); const mesh::IsGhostNode is_ghost(fs.nodes()); 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) { if (!is_ghost(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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/NodeColumnsInterface.h0000664000175000017500000004311715263665111027036 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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); 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/CubedSphereStructure.cc0000664000175000017500000001175115263665111027236 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/StructuredColumnsInterface.h0000664000175000017500000001300715263665111030310 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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__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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/StructuredColumns_create_remote_index.cc0000664000175000017500000002612015263665111032712 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/FunctionSpaceInterface.cc0000664000175000017500000001637015263665111027510 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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; } //------------------------------------------------------------------------------ field::FieldImpl* atlas__FunctionSpace__lonlat(const FunctionSpaceImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_FunctionSpace"); return This->lonlat().get(); } //------------------------------------------------------------------------------ field::FieldImpl* atlas__FunctionSpace__ghost(const FunctionSpaceImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_FunctionSpace"); return This->ghost().get(); } //------------------------------------------------------------------------------ field::FieldImpl* atlas__FunctionSpace__global_index(const FunctionSpaceImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_FunctionSpace"); return This->global_index().get(); } //------------------------------------------------------------------------------ field::FieldImpl* atlas__FunctionSpace__remote_index(const FunctionSpaceImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_FunctionSpace"); return This->remote_index().get(); } //------------------------------------------------------------------------------ field::FieldImpl* atlas__FunctionSpace__partition(const FunctionSpaceImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_FunctionSpace"); return This->partition().get(); } //------------------------------------------------------------------------------ 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); } //------------------------------------------------------------------------------ void atlas__FunctionSpace__checksum_field(const FunctionSpaceImpl* This, const field::FieldImpl* field, char*& checksum, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_FunctionSpace"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::string s = This->checksum(Field(field)); size = static_cast(s.size()); checksum = new char[size + 1]; std::strncpy(checksum, s.c_str(), size + 1); } //------------------------------------------------------------------------------ void atlas__FunctionSpace__checksum_fieldset(const FunctionSpaceImpl* This, const field::FieldSetImpl* fieldset, char*& checksum, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_FunctionSpace"); ATLAS_ASSERT(fieldset != nullptr, "Cannot access uninitialised atlas_FieldSet"); std::string s = This->checksum(FieldSet(fieldset)); size = static_cast(s.size()); checksum = new char[size + 1]; std::strncpy(checksum, s.c_str(), size + 1); } } // ------------------------------------------------------------------ } // namespace functionspace // ------------------------------------------------------------------ } // namespace atlas ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/SpectralInterface.h0000664000175000017500000000525615263665111026367 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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_base1(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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/StructuredColumns.h0000664000175000017500000002446215263665111026476 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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; } // 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 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 override; std::string checksum(const Field&) const override; 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_; } Field mask() const override; idx_t compute_j(idx_t j) const; 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::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; bool periodic_points_{false}; const StructuredGrid* grid_; mutable util::ObjectHandle gather_scatter_; 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_; mutable Field field_mask_; 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_; bool grid_is_regional_; 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/ColumnChecksum.h0000664000175000017500000002136515263665111025710 0ustar alastairalastair/* * (C) Copyright 2026- ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 "pluto/pluto.h" #include "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/parallel/GatherScatter.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/parallel/omp/omp.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Checksum.h" namespace atlas { namespace functionspace { namespace detail { /// @brief Shared checksum helper for column-based function spaces. /// /// This helper implements the lane-based checksum algorithm used by /// NodeColumns, EdgeColumns, and CellColumns. It is an internal utility in the /// functionspace detail layer and is not part of the public Atlas API. /// /// The class assumes the function space exposes the minimal interface needed by /// the algorithm: `size()`, `ghost()`, `gather()`, and `mpi_comm()`. /// /// @tparam FunctionSpace Internal function space implementation type. template class ColumnChecksum { public: /// @brief Construct a checksum helper for one function space instance. /// /// @param functionspace Internal function space instance providing entity /// ownership and gather operations. /// @param global_size Global number of entities represented on the root /// rank after gather. ColumnChecksum(const FunctionSpace& functionspace, size_t global_size): functionspace_(functionspace), global_size_(global_size) {} /// @brief Compute the checksum of a field set. /// /// The checksum is built from owned entities only, then gathered and /// reduced to a global hexadecimal digest. /// /// @param fieldset Field set defined on the associated function space. /// @return Hexadecimal checksum string. std::string compute(const FieldSet& fieldset) const { return util::checksum_to_hex_str(compute_checksum(fieldset)); } /// @brief Compute the checksum of a single field. /// /// This is a convenience overload that wraps the field into a temporary /// FieldSet and reuses the FieldSet-based implementation. /// /// @param field Field defined on the associated function space. /// @return Hexadecimal checksum string made of 4 characters (16 bits), see util::checksum_to_hex_str. std::string compute(const Field& field) const { FieldSet fieldset; fieldset.add(field); return compute(fieldset); } private: template static void checksum_update_point(util::checksum_t& checksum, const array::Array& field, const Value* data, idx_t dim, idx_t offset) { if (dim == field.rank()) { util::checksum_update(checksum, data + offset, size_t{1}); return; } for (idx_t index = 0; index < field.shape(dim); ++index) { checksum_update_point(checksum, field, data, dim + 1, offset + index * field.stride(dim)); } } static bool is_owned(const array::ArrayView& ghost, idx_t index) { return ghost(index) == 0; } static idx_t owned_contiguous_prefix_size(const array::ArrayView& ghost, idx_t size) { idx_t owned_size = size; while (owned_size > 0 && ghost(owned_size - 1) != 0) { --owned_size; } for (idx_t index = 0; index < owned_size; ++index) { if (ghost(index) != 0) { return -1; } } return owned_size; } template void update_lane_checksums_with_field(const Field& field, const array::ArrayView& ghost, util::checksum_t lane_checksums[], size_t lane_checksums_size) const { const idx_t npts = std::min(functionspace_.size(), field.shape(0)); ATLAS_ASSERT(lane_checksums_size >= static_cast(npts)); const auto* data = field.array().host_data(); if (field.contiguous()) { const auto point_stride = field.stride(0); const idx_t contiguous_npts = owned_contiguous_prefix_size(ghost, functionspace_.size()); if (contiguous_npts >= 0) { atlas_omp_parallel_for(idx_t index = 0; index < contiguous_npts; ++index) { util::checksum_update(lane_checksums[index], data + index * point_stride, point_stride); } } else { atlas_omp_parallel_for(idx_t index = 0; index < npts; ++index) { if (is_owned(ghost, index)) { util::checksum_update(lane_checksums[index], data + index * point_stride, point_stride); } } } return; } atlas_omp_parallel_for(idx_t index = 0; index < npts; ++index) { if (is_owned(ghost, index)) { util::checksum_t lane = lane_checksums[index]; checksum_update_point(lane, field, data, idx_t{1}, index * field.stride(0)); lane_checksums[index] = lane; } } } void update_lane_checksums_with_field_dynamic(const Field& field, const array::ArrayView& ghost, util::checksum_t lane_checksums[], size_t lane_checksums_size) const { switch (field.datatype().kind()) { case array::DataType::kind(): return update_lane_checksums_with_field(field, ghost, lane_checksums, lane_checksums_size); case array::DataType::kind(): return update_lane_checksums_with_field(field, ghost, lane_checksums, lane_checksums_size); case array::DataType::kind(): return update_lane_checksums_with_field(field, ghost, lane_checksums, lane_checksums_size); case array::DataType::kind(): return update_lane_checksums_with_field(field, ghost, lane_checksums, lane_checksums_size); default: throw_Exception("datatype not supported", Here()); } } util::checksum_t compute_checksum(const FieldSet& fieldset) const { pluto::scope scope; auto* memory_resource = pluto::host_pool_resource(); pluto::host::set_default_resource(memory_resource); const idx_t npts = functionspace_.size(); Field ghost_field = functionspace_.ghost(); auto ghost = array::make_view(ghost_field); std::vector> local_lanes(npts, 0, memory_resource); for (idx_t field_idx = 0; field_idx < fieldset.size(); ++field_idx) { update_lane_checksums_with_field_dynamic(fieldset[field_idx], ghost, local_lanes.data(), local_lanes.size()); } for (idx_t index = 0; index < npts; ++index) { if (is_owned(ghost, index)) { local_lanes[index] = util::checksum_digest(local_lanes[index]); } } const idx_t root = 0; auto& comm = mpi::comm(functionspace_.mpi_comm()); size_t nb_global = (comm.rank() == root) ? global_size_ : 0; std::unique_ptr> global_lanes( nb_global > 0 ? pluto::allocator(memory_resource).allocate(nb_global) : nullptr, [&](util::checksum_t* ptr) { if (ptr) { pluto::allocator(memory_resource).deallocate(ptr, nb_global); } }); { mpi::Scope mpi_scope(functionspace_.mpi_comm()); idx_t lvar_stride = 1; idx_t lvar_shape = 1; idx_t gvar_stride = 1; idx_t gvar_shape = 1; functionspace_.gather().gather(local_lanes.data(), &lvar_stride, &lvar_shape, 1, global_lanes.get(), &gvar_stride, &gvar_shape, 1, root); } util::checksum_t global_checksum = 0; if (comm.rank() == root) { global_checksum = util::checksum(global_lanes.get(), nb_global); } comm.broadcast(global_checksum, root); return global_checksum; } private: const FunctionSpace& functionspace_; size_t global_size_; }; } // namespace detail } // namespace functionspace } // namespace atlasecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/CellColumnsInterface.cc0000664000175000017500000001300715263665111027161 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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->deprecated_checksum(); } } // extern C } // namespace detail } // namespace functionspace } // namespace atlas ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/BlockStructuredColumnsInterface.cc0000664000175000017500000002260515263665111031425 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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__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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/CellColumnsInterface.h0000664000175000017500000000467215263665111027033 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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); const parallel::Checksum* atlas__CellsFunctionSpace__get_checksum(const CellColumns* This); } } // namespace detail } // namespace functionspace } // namespace atlas ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/StructuredColumns.cc0000664000175000017500000011373615263665111026637 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/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/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()); } } } struct ChecksumData { Field local_lane_checksums; Field global_lane_checksums; int root{0}; }; template void checksum_update_point(util::checksum_t& checksum, const array::Array& f, const T* data, idx_t dim, idx_t offset) { if (dim == f.rank()) { util::checksum_update(checksum, data + offset, size_t{1}); return; } for (idx_t index = 0; index < f.shape(dim); ++index) { checksum_update_point(checksum, f, data, dim + 1, offset + index * f.stride(dim)); } } template void update_lane_checksums_with_field_contiguous(const StructuredColumns& fs, const Field& f, const T* data, array::ArrayView& local_lane_checksums_view) { const idx_t npts = fs.sizeOwned(); if (f.rank() == 3) { const idx_t nlev = f.shape(1); const idx_t nvar = f.shape(2); atlas_omp_parallel_for(idx_t point = 0; point < npts; ++point) { util::checksum_t lane_checksum = local_lane_checksums_view(point); const T* point_data = data + point * f.stride(0); for (idx_t jlev = 0; jlev < nlev; ++jlev) { util::checksum_update(lane_checksum, point_data + jlev * f.stride(1), static_cast(nvar)); } local_lane_checksums_view(point) = lane_checksum; } } else if (f.rank() == 2) { const size_t lane_size = static_cast(f.shape(1)); atlas_omp_parallel_for(idx_t point = 0; point < npts; ++point) { util::checksum_update(local_lane_checksums_view(point), data + point * f.stride(0), lane_size); } } else if (f.rank() == 1) { atlas_omp_parallel_for(idx_t point = 0; point < npts; ++point) { util::checksum_update(local_lane_checksums_view(point), data + point, size_t{1}); } } else { ATLAS_NOTIMPLEMENTED; } } template void update_lane_checksums_with_field(const StructuredColumns& fs, const Field& f, ChecksumData& checksum_data) { auto local_lane_checksums_view = array::make_view(checksum_data.local_lane_checksums); const auto* data = f.array().host_data(); if (f.contiguous()) { update_lane_checksums_with_field_contiguous(fs, f, data, local_lane_checksums_view); return; } const idx_t npts = fs.sizeOwned(); atlas_omp_parallel_for(idx_t point = 0; point < npts; ++point) { util::checksum_t lane_checksum = local_lane_checksums_view(point); checksum_update_point(lane_checksum, f, data, idx_t{1}, point * f.stride(0)); local_lane_checksums_view(point) = lane_checksum; } } void update_lane_checksums_with_field_dynamic(const StructuredColumns& fs, const Field& f, ChecksumData& checksum_data) { switch (f.datatype().kind()) { case array::DataType::kind(): return update_lane_checksums_with_field(fs, f, checksum_data); case array::DataType::kind(): return update_lane_checksums_with_field(fs, f, checksum_data); case array::DataType::kind(): return update_lane_checksums_with_field(fs, f, checksum_data); case array::DataType::kind(): return update_lane_checksums_with_field(fs, f, checksum_data); default: throw_Exception("datatype not supported", Here()); } } ChecksumData get_checksum_data(const StructuredColumns& fs) { pluto::scope scope; pluto::host::set_default_resource(pluto::host_pool_resource()); ChecksumData checksum_data; checksum_data.root = 0; checksum_data.local_lane_checksums = fs.createField(option::name("checksum_local_lane_checksums") | option::datatypeT() | option::levels(0)); checksum_data.global_lane_checksums = fs.createField(checksum_data.local_lane_checksums, option::global(checksum_data.root)); auto local_lane_checksums_view = array::make_view(checksum_data.local_lane_checksums); local_lane_checksums_view.assign(0); return checksum_data; } util::checksum_t checksum_from_lane_states(const StructuredColumns& fs, ChecksumData& checksum_data) { auto& local_lane_checksums = checksum_data.local_lane_checksums; auto local_lane_checksums_view = array::make_view(local_lane_checksums); for (idx_t point = 0; point < fs.sizeOwned(); ++point) { local_lane_checksums_view(point) = util::checksum_digest(local_lane_checksums_view(point)); } auto& mpi_comm = mpi::comm(fs.mpi_comm()); if (mpi_comm.size() == 1) { ATLAS_ASSERT(local_lane_checksums.contiguous()); const auto* local_lane_checksums_data = local_lane_checksums.array().host_data(); return util::checksum(local_lane_checksums_data, static_cast(fs.sizeOwned())); } auto& global_lane_checksums = checksum_data.global_lane_checksums; fs.gather(local_lane_checksums, global_lane_checksums); util::checksum_t global_checksum = 0; if (mpi_comm.rank() == checksum_data.root) { ATLAS_ASSERT(global_lane_checksums.contiguous()); const auto* global_lane_checksums_data = global_lane_checksums.array().host_data(); global_checksum = util::checksum(global_lane_checksums_data, global_lane_checksums.size()); } mpi_comm.broadcast(global_checksum, checksum_data.root); return global_checksum; } util::checksum_t field_checksum(const StructuredColumns& fs, const Field& field) { auto checksum_data = get_checksum_data(fs); update_lane_checksums_with_field_dynamic(fs, field, checksum_data); return checksum_from_lane_states(fs, checksum_data); } util::checksum_t fieldset_checksum(const StructuredColumns& fs, const FieldSet& fieldset) { auto checksum_data = get_checksum_data(fs); for (idx_t f = 0; f < fieldset.size(); ++f) { update_lane_checksums_with_field_dynamic(fs, fieldset[f], checksum_data); } return checksum_from_lane_states(fs, checksum_data); } } // 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[name=" << funcspace.grid().name() << ",address=" << funcspace.grid().get() << "]" << "functionspace[halo=" << funcspace.halo() << ",periodic_points=" << std::boolalpha << funcspace.periodic_points_ << ",distribution=" << funcspace.distribution() << "]" << ",comm[name=" << funcspace.mpi_comm() << ",address=" << &mpi::comm(funcspace.mpi_comm()) << "]"; 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[name=" << funcspace.grid().name() << ",address=" << funcspace.grid().get() << "]" << "functionspace[halo=" << funcspace.halo() << ",periodic_points=" << std::boolalpha << funcspace.periodic_points_ << ",distribution=" << funcspace.distribution() << "]" << ",comm[name=" << funcspace.mpi_comm() << ",address=" << &mpi::comm(funcspace.mpi_comm()) << "]"; 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; }; 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::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); } // ---------------------------------------------------------------------------- idx_t StructuredColumns::compute_j(idx_t j) const { if (j < 0) { return grid_is_regional_ ? -j : -j - 1 + north_pole_included_; } if (j >= ny_) { return grid_is_regional_ ? 2 * ny_ - j - 2 : 2 * ny_ - j - 1 - south_pole_included_; } return j; } void StructuredColumns::compute_xy(idx_t i, idx_t j, PointXY& xy) const { const idx_t jj = compute_j(j); if (j < 0) { if (grid_is_regional_) { xy[YY] = 2. * grid_->y(0) - grid_->y(jj); } else { xy[YY] = 180. - grid_->y(jj); } } else if (j >= ny_) { if (grid_is_regional_) { xy[YY] = 2. * grid_->y(ny_ - 1) - grid_->y(jj); } else { xy[YY] = -180. - grid_->y(jj); } } else { 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::name(other.name()) | 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 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, mpi_comm()); 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 { return util::checksum_to_hex_str(fieldset_checksum(*this, fieldset)); } std::string StructuredColumns::checksum(const Field& field) const { return util::checksum_to_hex_str(field_checksum(*this, field)); } 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; } Field StructuredColumns::mask() const { if (!field_mask_) { field_mask_ = createField(option::name("mask")|option::datatypeT()|option::levels(0)); array::make_view(field_mask_).assign(1); } return field_mask_; } // ---------------------------------------------------------------------------- } // namespace detail } // namespace functionspace } // namespace atlas ecmwf-atlas-b0f79ee/src/atlas/functionspace/detail/NodeColumnsInterface.cc0000664000175000017500000014250115263665111027171 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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->deprecated_checksum(); } 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/Spectral.cc0000664000175000017500000005412515263665111023441 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 { class Spectral::Parallelisation { public: Parallelisation() = default; virtual ~Parallelisation() {} virtual int nb_spectral_coefficients_global() const = 0; virtual int nb_spectral_coefficients() const = 0; virtual int nump() const = 0; virtual array::LocalView nvalue() const = 0; virtual array::LocalView nmyms() const = 0; virtual array::LocalView nasm0_base0() const = 0; virtual array::LocalView nasm0_base1() const = 0; virtual std::string distribution() const = 0; }; class Parallelisation_local : public Spectral::Parallelisation { public: Parallelisation_local(int truncation): truncation_(truncation) { // Assume serial!!! nspec2g_ = (truncation_ + 1) * (truncation_ + 2); nmyms_.resize(truncation_ + 1); nasm0_base0_.resize(truncation_ + 1); nasm0_base1_.resize(truncation_ + 1); nvalue_.resize(nspec2g_); idx_t jc{0}; for (idx_t m = 0; m <= truncation_; ++m) { nmyms_[m] = m; nasm0_base0_[m] = jc; // zero-based index nasm0_base1_[m] = jc + 1; // Fortran index for (idx_t n = m; n <= truncation_; ++n) { nvalue_[jc++] = n; nvalue_[jc++] = n; } } ATLAS_ASSERT(jc == nspec2g_); } int nb_spectral_coefficients_global() const override { return nspec2g_; } int nb_spectral_coefficients() const override { return nb_spectral_coefficients_global(); } int truncation_; std::string distribution() const override { return "serial"; } int nump() const override { return truncation_ + 1; } array::LocalView nmyms() const override { return array::make_view(nmyms_.data(), array::make_shape(nump())); } array::LocalView nvalue() const override { return array::make_view(nvalue_.data(), array::make_shape(nb_spectral_coefficients())); } array::LocalView nasm0_base0() const override { return array::make_view(nasm0_base0_.data(), array::make_shape(truncation_ + 1)); } array::LocalView nasm0_base1() const override { return array::make_view(nasm0_base1_.data(), array::make_shape(truncation_ + 1)); } int nspec2g_; std::vector nmyms_; std::vector nasm0_base0_; // zero-based offsets for use in C++ std::vector nasm0_base1_; // one-based offsets for use in Fortran std::vector nvalue_; }; #if ATLAS_HAVE_TRANS class Parallelisation_ectrans : public Spectral::Parallelisation { public: Parallelisation_ectrans(int truncation, const std::string& mpi_comm) { const auto& comm = mpi::comm(mpi_comm); 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(comm.communicator())); #endif TRANS_CHECK(::trans_new(trans_.get())); TRANS_CHECK(::trans_set_trunc(trans_.get(), truncation)); TRANS_CHECK(::trans_use_mpi(comm.size() > 1)); TRANS_CHECK(::trans_setup(trans_.get())); } virtual ~Parallelisation_ectrans() {} int nb_spectral_coefficients_global() const override { return trans_->nspec2g; } int nb_spectral_coefficients() const override { return trans_->nspec2; } int nump() const override { return trans_->nump; } array::LocalView nvalue() const override { if (trans_->nvalue == nullptr) { ::trans_inquire(trans_.get(), "nvalue"); } return array::make_view(trans_->nvalue, array::make_shape(trans_->nspec2)); } array::LocalView nmyms() const override { if (trans_->nmyms == nullptr) { ::trans_inquire(trans_.get(), "nmyms"); } return array::make_view(trans_->nmyms, array::make_shape(nump())); } void setup_nasm0() const { if (trans_->nasm0 == nullptr) { ::trans_inquire(trans_.get(), "nasm0"); } nasm0_base0_.resize(trans_->nsmax + 1); nasm0_base1_.resize(trans_->nsmax + 1); for (int m = 0; m <= trans_->nsmax; ++m) { nasm0_base0_[m] = trans_->nasm0[m] - 1; // convert to zero-based indexing nasm0_base1_[m] = trans_->nasm0[m]; // one-based indexing as in Fortran } } array::LocalView nasm0_base0() const override { if (nasm0_base0_.empty()) { setup_nasm0(); } return array::make_view(nasm0_base0_.data(), array::make_shape(trans_->nsmax + 1)); } array::LocalView nasm0_base1() const override { if (nasm0_base1_.empty()) { setup_nasm0(); } return array::make_view(nasm0_base1_.data(), array::make_shape(trans_->nsmax + 1)); } std::string distribution() const override { return "ectrans"; } operator ::Trans_t*() const { return trans_.get(); } std::shared_ptr<::Trans_t> trans_; mutable std::vector nasm0_base0_; // zero-based offsets mutable std::vector nasm0_base1_; // one-based offsets as in Fortran }; #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); const auto& comm = mpi::comm(mpi_comm_); size = (comm.size() == 0 || idx_t(comm.rank()) == owner ? nb_spectral_coefficients_global() : 0); } } return size; } // ---------------------------------------------------------------------- Spectral::Spectral(const eckit::Configuration& config): Spectral::Spectral(config.getUnsigned("truncation"), config) {} // ---------------------------------------------------------------------- namespace { std::string get_mpi_comm(const eckit::Configuration& config) { return config.getString("mpi_comm", mpi::comm().name()); } [[maybe_unused]] bool is_backend_local(const eckit::Configuration& config) { std::string backend; if (config.get("backend", backend)) { if (backend == "local") { return true; } } return false; } } Spectral::Spectral(const int truncation, const eckit::Configuration& config): mpi_comm_(get_mpi_comm(config)), nb_levels_(0), truncation_(truncation), #if ATLAS_HAVE_TRANS parallelisation_(is_backend_local(config) ? static_cast(new Parallelisation_local(truncation_)) : static_cast(new Parallelisation_ectrans(truncation_, mpi_comm_))) #else parallelisation_(new Parallelisation_local(truncation_)) #endif { config.get("levels", nb_levels_); } Spectral::Spectral(const long truncation, const eckit::Configuration& config) : Spectral(static_cast(truncation), config) { } Spectral::Spectral(const size_t truncation, const eckit::Configuration& config) : Spectral(static_cast(truncation), config) { } Spectral::~Spectral() = default; std::string Spectral::distribution() const { return parallelisation_->distribution(); } idx_t Spectral::part() const { return mpi::comm(mpi_comm_).rank(); } idx_t Spectral::nb_parts() const { return mpi::comm(mpi_comm_).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::name(other.name()) | option::datatype(other.datatype()) | option::levels(other.levels()) | config); } namespace { // Workaround for ectrans version < 2.1 because of problem with // zero-sized spectral data arrays, see https://github.com/ecmwf-ifs/ectrans/pull/476 [[maybe_unused]] double* zero_size_spectral_data() { static double value = 0.; return &value; } [[maybe_unused]] double* spectral_data(Field& field) { double* data = field.array().data(); return data == nullptr && field.shape(0) == 0 ? zero_size_spectral_data() : data; } [[maybe_unused]] const double* spectral_data(const Field& field) { const double* data = field.array().data(); return data == nullptr && field.shape(0) == 0 ? zero_size_spectral_data() : data; } } // namespace 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::comm(mpi_comm_).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"); } const Parallelisation_ectrans* parallelisation_ectrans = dynamic_cast(parallelisation_.get()); if (parallelisation_ectrans == nullptr) { throw_Exception( "Cannot gather spectral fields because Spectral functionspace is not using ectrans."); } struct ::GathSpec_t args = new_gathspec(*parallelisation_ectrans); args.nfld = nto.size(); args.rspecg = glb.array().data(); args.nto = nto.data(); args.rspec = spectral_data(loc); 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::comm(mpi_comm_).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"); } const Parallelisation_ectrans* parallelisation_ectrans = dynamic_cast(parallelisation_.get()); if (parallelisation_ectrans == nullptr) { throw_Exception( "Cannot gather spectral fields because Spectral functionspace is not using ectrans."); } struct ::DistSpec_t args = new_distspec(*parallelisation_ectrans); args.nfld = int(nfrom.size()); args.rspecg = glb.array().data(); args.nfrom = nfrom.data(); args.rspec = spectral_data(loc); TRANS_CHECK(::trans_distspec(&args)); glb.metadata().broadcast(loc.metadata(), root, mpi_comm_); 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 { 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."); const Parallelisation_ectrans* parallelisation_ectrans = dynamic_cast(parallelisation_.get()); if (parallelisation_ectrans == nullptr) { throw_Exception( "Cannot gather spectral fields because Spectral functionspace is not using ectrans."); } struct ::SpecNorm_t args = new_specnorm(*parallelisation_ectrans); args.nfld = 1; args.rspec = spectral_data(field); 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"); } const Parallelisation_ectrans* parallelisation_ectrans = dynamic_cast(parallelisation_.get()); if (parallelisation_ectrans == nullptr) { throw_Exception( "Cannot gather spectral fields because Spectral functionspace is not using ectrans."); } struct ::SpecNorm_t args = new_specnorm(*parallelisation_ectrans); args.nfld = std::max(1, field.levels()); args.rspec = spectral_data(field); 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(); } array::LocalView Spectral::offsets_by_zonal_wavenumber() const { return parallelisation_->nasm0_base0(); } 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_base0() const { return parallelisation_->nasm0_base0(); } array::LocalView Spectral::nasm0_base1() const { return parallelisation_->nasm0_base1(); } } // 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())) {} Spectral::Spectral(const long truncation, const eckit::Configuration& config): FunctionSpace(new detail::Spectral(truncation, config)), functionspace_(dynamic_cast(get())) {} Spectral::Spectral(const size_t 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(); } array::LocalView Spectral::offsets_by_zonal_wavenumber() const { return functionspace_->offsets_by_zonal_wavenumber(); } // ---------------------------------------------------------------------- } // namespace functionspace } // namespace atlas ecmwf-atlas-b0f79ee/src/atlas/functionspace/CubedSphereColumns.cc0000664000175000017500000001346015263665111025413 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/PointCloud.h0000664000175000017500000002044315263665111023602 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 mask() 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_; mutable Field mask_; 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/FunctionSpace.h0000664000175000017500000000754515263665111024273 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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" 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; std::string checksum(const FieldSet&) const; std::string checksum(const 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; Field mask() const; bool hasMask() const; void setMask(const Field& global_mask); using MaskInitializer = std::function; void setMask(const MaskInitializer&, idx_t root = 0); 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/FunctionSpace.cc0000664000175000017500000001261315263665111024421 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/FunctionSpace.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/grid.h" #include "atlas/functionspace/detail/FunctionSpaceImpl.h" #include "atlas/option/Options.h" #include "atlas/parallel/mpi/mpi.h" namespace atlas { FunctionSpace::FunctionSpace(): Handle(new functionspace::NoFunctionSpace()) {} std::string FunctionSpace::type() const { return get()->type(); } 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(); } Field FunctionSpace::mask() const { return get()->mask(); } bool FunctionSpace::hasMask() const { return get()->hasMask(); } void FunctionSpace::setMask(const Field& global_mask) { get()->setMask(global_mask); } void FunctionSpace::setMask(const MaskInitializer& initialize, idx_t root) { Field global_mask = createField(option::name("mask") | option::global(root)); if (mpi::comm(mpi_comm()).rank() == root) { auto global_mask_view = array::make_view(global_mask); initialize(global_mask_view.data(), global_mask_view.size()); } setMask(global_mask); } 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::checksum(const FieldSet& fieldset) const { return get()->checksum(fieldset); } std::string FunctionSpace::checksum(const Field& field) const { return get()->checksum(field); } 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/CellColumns.h0000664000175000017500000001734515263665111023751 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 override; std::string checksum(const Field&) const override; [[deprecated("Use FunctionSpaceImpl::checksum(const FieldSet&) or FunctionSpaceImpl::checksum(const Field&) instead")]] const parallel::Checksum& checksum() const; // Just for internal API at the moment. DO NOT USE! const parallel::Checksum& deprecated_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; Field mask() 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_; mutable Field field_mask_; }; // ------------------------------------------------------------------- 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); 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; using FunctionSpace::checksum; [[deprecated("Use FunctionSpace::checksum(const FieldSet&) or FunctionSpace::checksum(const Field&) instead")]] const parallel::Checksum& checksum() const; private: const detail::CellColumns* functionspace_; }; } // namespace functionspace } // namespace atlas ecmwf-atlas-b0f79ee/src/atlas/functionspace/NodeColumns.h0000664000175000017500000010220415263665111023744 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 override; std::string checksum(const Field&) const override; [[deprecated("Use FunctionSpaceImpl::checksum(const FieldSet&) or FunctionSpaceImpl::checksum(const Field&) instead")]] const parallel::Checksum& checksum() const; // Just for internal API at the moment. DO NOT USE! const parallel::Checksum& deprecated_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(); } Field mask() const override; 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_; mutable Field field_mask_; 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; using FunctionSpace::checksum; [[deprecated("Use FunctionSpace::checksum(const FieldSet&) or FunctionSpace::checksum(const Field&) instead")]] 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/BlockStructuredColumns.h0000664000175000017500000000751015263665111026202 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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& fieldset) const { return functionspace_->checksum(fieldset); } std::string checksum(const Field& field) const { return functionspace_->checksum(field); } 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/EdgeColumns.h0000664000175000017500000001651115263665111023730 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 override; std::string checksum(const Field&) const override; [[deprecated("Use FunctionSpaceImpl::checksum(const FieldSet&) or FunctionSpaceImpl::checksum(const Field&) instead")]] const parallel::Checksum& checksum() const; // Just for internal API at the moment. DO NOT USE! const parallel::Checksum& deprecated_checksum() const; idx_t size() const override { return nb_edges_; } const Grid& grid() const override; Field lonlat() const override; Field ghost() 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); 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; using FunctionSpace::checksum; [[deprecated("Use FunctionSpace::checksum(const FieldSet&) or FunctionSpace::checksum(const Field&) instead")]] const parallel::Checksum& checksum() const; private: const detail::EdgeColumns* functionspace_; }; } // namespace functionspace } // namespace atlas ecmwf-atlas-b0f79ee/src/atlas/functionspace/Locate.cc0000664000175000017500000000671015263665111023070 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/Locate.h0000664000175000017500000000222615263665111022730 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/PointCloud.cc0000664000175000017500000013032515263665111023741 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(); max_glb_idx_ = 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_); auto ghost = array::make_view(ghost_); auto partition = array::make_view(partition_); ATLAS_ASSERT(grid.size() == distribution.size()); idx_t j{0}; gidx_t g{0}; bool has_intrinsic_ghosts{false}; for (auto p : grid.lonlat()) { if( distribution.partition(g) == part_ ) { ATLAS_ASSERT(j < size_halo); gidx(j) = g+1; ridx(j) = j; ghost(j) = GridPointFlags{grid.flags(g)}.has(GridPointFlags::duplicate); const auto master = grid.masterIndex(g); partition(j) = distribution.partition(master); lonlat(j, 0) = p.lon(); lonlat(j, 1) = p.lat(); has_intrinsic_ghosts = has_intrinsic_ghosts || ghost(j); ++j; } ++g; } if (has_intrinsic_ghosts) { remote_index_ = Field(); } } 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)); 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]) { const auto master = grid.masterIndex(g); lonlat(j, 0) = p.lon(); lonlat(j, 1) = p.lat(); partition(j) = distribution.partition(master); ghost(j) = GridPointFlags{grid.flags(g)}.has(GridPointFlags::duplicate) || 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, mpi_comm()); 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::name(other.name()) |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 master_global_index = [this, &g](idx_t index) { return grid_ ? grid_.masterIndex(g(index) - 1) + 1 : g(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(), bool(grid_)); } } 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); } Field PointCloud::mask() const { if (!mask_) { mask_ = createField(option::name("mask") | option::datatypeT() | option::levels(0)); array::make_view(mask_).assign(1); } return mask_; } } // 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/NodeColumns.cc0000664000175000017500000006215215263665111024111 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 "pluto/pluto.h" #include "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/detail/ColumnChecksum.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/Checksum.h" #include "atlas/util/detail/Cache.h" #include "atlas/util/vector.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()); atlas::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, mpi_comm()); 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); } std::string NodeColumns::checksum(const FieldSet& fieldset) const { return ColumnChecksum(*this, static_cast(nb_nodes_global())).compute(fieldset); } std::string NodeColumns::checksum(const Field& field) const { return ColumnChecksum(*this, static_cast(nb_nodes_global())).compute(field); } const parallel::Checksum& NodeColumns::checksum() const { return deprecated_checksum(); } const parallel::Checksum& NodeColumns::deprecated_checksum() const { if (checksum_) { return *checksum_; } checksum_ = NodeColumnsChecksumCache::instance().get_or_create(mesh_); return *checksum_; } Field NodeColumns::mask() const { if (!field_mask_) { field_mask_ = createField(option::name("mask") | option::datatypeT() | option::levels(0)); array::make_view(field_mask_).assign(1); } return field_mask_; } // 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(); } const parallel::Checksum& NodeColumns::checksum() const { return functionspace_->deprecated_checksum(); } } // namespace functionspace } // namespace atlas ecmwf-atlas-b0f79ee/src/atlas/functionspace/StructuredColumns.h0000664000175000017500000002265515263665111025236 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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& fieldset) const { return functionspace_->checksum(fieldset); } std::string checksum(const Field& field) const { return functionspace_->checksum(field); } 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(); } Field mask() const { return functionspace_->mask(); } idx_t compute_j(idx_t j) const { return functionspace_->compute_j(j); } 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 ecmwf-atlas-b0f79ee/src/atlas/functionspace/StructuredColumns.cc0000664000175000017500000000543015263665111025364 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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())) {} // ---------------------------------------------------------------------------- } // namespace functionspace } // namespace atlas ecmwf-atlas-b0f79ee/src/atlas/functionspace/HaloDescription.h0000664000175000017500000000172415263665111024612 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; i #include #include #include "eckit/utils/MD5.h" #include "atlas/array/MakeView.h" #include "atlas/functionspace/detail/ColumnChecksum.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(); } 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); } Field CellColumns::mask() const { if (!field_mask_) { field_mask_ = createField(option::name("mask") | option::datatypeT() | option::levels(0)); array::make_view(field_mask_).assign(1); } return field_mask_; } 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, mpi_comm()); 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); } std::string CellColumns::checksum(const FieldSet& fieldset) const { return ColumnChecksum(*this, static_cast(nb_cells_global())).compute(fieldset); } std::string CellColumns::checksum(const Field& field) const { return ColumnChecksum(*this, static_cast(nb_cells_global())).compute(field); } const parallel::Checksum& CellColumns::checksum() const { return deprecated_checksum(); } const parallel::Checksum& CellColumns::deprecated_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->deprecated_checksum(); } // ----------------------------------------------------------------------------------- } // ----------------------------------------------------------------------------------- } // 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(); } const parallel::Checksum& CellColumns::checksum() const { return functionspace_->deprecated_checksum(); } const mesh::Halo& CellColumns::halo() const { return functionspace_->halo(); } } // namespace functionspace } // namespace atlas ecmwf-atlas-b0f79ee/src/atlas/interpolation/0000775000175000017500000000000015263665111021374 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/interpolation/NonLinear.h0000664000175000017500000000417015263665111023434 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/Interpolation.cc0000664000175000017500000001657615263665111024551 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/Vector2D.h0000664000175000017500000000514415263665111023201 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/Interpolation.h0000664000175000017500000001100015263665111024364 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/Vector3D.h0000664000175000017500000000612615263665111023203 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/Vector3D.cc0000664000175000017500000000123415263665111023334 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/nonlinear/0000775000175000017500000000000015263665111023361 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/interpolation/nonlinear/NonLinear.h0000664000175000017500000000641515263665111025425 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/nonlinear/NonLinear.cc0000664000175000017500000000144515263665111025561 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/nonlinear/Missing.h0000664000175000017500000000355515263665111025153 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/nonlinear/Missing.cc0000664000175000017500000002604015263665111025303 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/AssembleGlobalMatrix.cc0000664000175000017500000003255715263665111025760 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/Cache.cc0000664000175000017500000001071415263665111022711 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/NonLinear.cc0000664000175000017500000000323015263665111023566 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/0000775000175000017500000000000015263665111022654 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/interpolation/method/Method.cc0000664000175000017500000007345515263665111024421 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/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. // // Both the non-linear missing-value redistribution (nonlinear/Missing.cc) and the subsequent sparse matmul // (interpolate_field_rank1) only ever read the source at the columns referenced by the weights `W` (the stencil // support). We therefore only need to populate those referenced source rows in the per-level temporary, not the // entire source column. This turns the per-call copy from O(source_size * levels) into O(nnz * levels) == // O(target_size * stencil * levels); for target sizes much smaller than the source (e.g. observation operators) // this eliminates the dominant, obs-independent apply cost. // 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 observation-operator-like interpolations (target << source) most source rows are unreferenced, so // gathering only the referenced rows per level avoids copying the full source column. For regridding-like // interpolations (target ~ source, nearly every source row referenced) the referenced set approaches the full // source and the extra sort/deduplicate below would be pure overhead. Use nnz vs source size as a cheap proxy (an // average of >= 1 nonzero per source row means most of the source is referenced) to fall back to the plain // full-column copy in that regime, keeping the regridding path free of the O(nnz log nnz) set construction. auto W_v = make_host_view_r(W); const bool gather_referenced = static_cast(W_v.nnz()) < src.shape(0); // Unique set of source rows (= matrix columns) referenced by the weights; only built on the gather path. std::vector referenced_rows; if (gather_referenced) { referenced_rows.assign(W_v.inner(), W_v.inner() + W_v.nnz()); std::sort(referenced_rows.begin(), referenced_rows.end()); referenced_rows.erase(std::unique(referenced_rows.begin(), referenced_rows.end()), referenced_rows.end()); } for (idx_t lev = 0; lev < src_v.shape(1); ++lev) { // Copy this level to the temporary rank-1 field: only the referenced source rows when gathering, // otherwise the full source column. if (gather_referenced) { for (const auto row : referenced_rows) { src_slice_v(row) = src_v(row, lev); } } else { 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(); } if (not W.empty() && nonLinear_(src)) { // As for rank-2 (see interpolate_field_rank2), a non-linear update cannot be applied to whole columns at once // because missing values may be present in only part of a column. A rank-3 source is laid out as // (source, dim1, dim2), so we decompose it into rank-1 slices over the two trailing dimensions and interpolate // each slice independently. Only the source rows referenced by the weights `W` are ever read (by both the // non-linear redistribution and the matmul), so we gather only those rows into the per-slice temporary; see // interpolate_field_rank2 for the full rationale and the regridding fall-back. // Allocate temporary rank-1 fields corresponding to one slice over the trailing dimensions 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 (needed e.g. for the missing value) 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); // Fall back to the full-column copy when most of the source is referenced (regridding-like); see rank-2. auto W_v = make_host_view_r(W); const bool gather_referenced = static_cast(W_v.nnz()) < src.shape(0); // Unique set of source rows (= matrix columns) referenced by the weights; only built on the gather path. std::vector referenced_rows; if (gather_referenced) { referenced_rows.assign(W_v.inner(), W_v.inner() + W_v.nnz()); std::sort(referenced_rows.begin(), referenced_rows.end()); referenced_rows.erase(std::unique(referenced_rows.begin(), referenced_rows.end()), referenced_rows.end()); } for (idx_t j = 0; j < src_v.shape(1); ++j) { for (idx_t k = 0; k < src_v.shape(2); ++k) { // Copy this slice to the temporary rank-1 field: only the referenced source rows when gathering, // otherwise the full source column. if (gather_referenced) { for (const auto row : referenced_rows) { src_slice_v(row) = src_v(row, j, k); } } else { for (idx_t i = 0; i < src.shape(0); ++i) { src_slice_v(i) = src_v(i, j, k); } } // Interpolate between rank-1 fields interpolate_field_rank1(src_slice, tgt_slice, W); // Copy rank-1 field back to this slice in the rank-3 field tgt_slice.syncHost(); for (idx_t i = 0; i < tgt.shape(0); ++i) { tgt_v(i, j, k) = tgt_slice_v(i); } } } tgt.setDeviceNeedsUpdate(true); tgt.setHostNeedsUpdate(false); } else { auto W_v = make_host_view(W); auto src_v = make_host_view_r(src); auto tgt_v = make_host_view_w(tgt); 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); config.get("allow_halo_exchange", allow_halo_exchange_ = true); } 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 for field "+src.name()+" (hostNeedsUpdate && deviceNeedsUpdate) - 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/cubedsphere/0000775000175000017500000000000015263665111025145 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/interpolation/method/cubedsphere/CubedSphereBilinear.cc0000664000175000017500000000746015263665111031322 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/cubedsphere/CellFinder.cc0000664000175000017500000001116415263665111027466 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/cubedsphere/CubedSphereBilinear.h0000664000175000017500000000375715263665111031171 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/cubedsphere/CellFinder.h0000664000175000017500000000351415263665111027330 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/Intersect.h0000664000175000017500000000245015263665111024766 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/Intersect.cc0000664000175000017500000000145215263665111025125 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/PointSet.cc0000664000175000017500000000654515263665111024742 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/0000775000175000017500000000000015263665111025060 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/Cubic2D.cc0000664000175000017500000000220015263665111026574 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/QuasiCubic2D.h0000664000175000017500000000141415263665111027447 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/Linear3D.cc0000664000175000017500000000221415263665111026767 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/Linear3D.h0000664000175000017500000000135415263665111026635 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/StructuredInterpolation2D.h0000664000175000017500000000511215263665111032332 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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" ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/StructuredInterpolation3D.h0000664000175000017500000000471615263665111032344 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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" ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/StructuredInterpolation3D.tcc0000664000175000017500000003113615263665111032662 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/Linear2D.cc0000664000175000017500000000221715263665111026771 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/Linear2D.h0000664000175000017500000000137415263665111026636 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/kernels/0000775000175000017500000000000015263665111026523 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/kernels/Cubic3DKernel.h0000664000175000017500000002257315263665111031262 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/kernels/CubicHorizontalLimiter.h0000664000175000017500000001357315263665111033332 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/kernels/QuasiCubic3DKernel.h0000664000175000017500000003710315263665111032260 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/kernels/QuasiCubic3DKernel.cc0000664000175000017500000000167015263665111032416 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/kernels/CubicHorizontalKernel.h0000664000175000017500000003153615263665111033144 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/kernels/LinearVerticalKernel.h0000664000175000017500000000657215263665111032753 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/kernels/Linear3DKernel.h0000664000175000017500000002136615263665111031446 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/kernels/CubicVerticalKernel.h0000664000175000017500000001634415263665111032564 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/kernels/QuasiCubicHorizontalKernel.h0000664000175000017500000003752015263665111034146 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/kernels/LinearHorizontalKernel.h0000664000175000017500000002511415263665111033324 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/kernels/Cubic3DLimiter.h0000664000175000017500000001070115263665111031435 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 // k0 k1 k2 k3 // k_interval: <-- -1 |-----0-----|-----1-----|----2----| 3 --> // using Value = typename OutputArray::value_type; const idx_t k_interval = stencil.k_interval(); idx_t k1, k2; if (k_interval < 1) { k1 = stencil.k(0); k2 = stencil.k(1); } else if (k_interval > 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/RegionalLinear2D.cc0000664000175000017500000005357215263665111030464 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/StructuredInterpolation2D.tcc0000664000175000017500000005550415263665111032666 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/QuasiCubic3D.cc0000664000175000017500000000231015263665111027602 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/Cubic3D.cc0000664000175000017500000000217715263665111026612 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/QuasiCubic2D.cc0000664000175000017500000000231315263665111027604 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/Cubic3D.h0000664000175000017500000000135015263665111026444 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/RegionalLinear2D.h0000664000175000017500000000454715263665111030324 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/Cubic2D.h0000664000175000017500000000137015263665111026445 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/structured/QuasiCubic3D.h0000664000175000017500000000137415263665111027455 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/knn/0000775000175000017500000000000015263665111023442 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/interpolation/method/knn/GridBoxMaximum.h0000664000175000017500000000162115263665111026507 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/knn/GridBoxAverage.cc0000664000175000017500000000563515263665111026613 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/knn/NearestNeighbour.h0000664000175000017500000000331015263665111027054 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/knn/KNearestNeighbours.cc0000664000175000017500000001162515263665111027520 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/knn/GridBoxAverage.h0000664000175000017500000000162115263665111026444 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/knn/GridBoxMethod.h0000664000175000017500000000451515263665111026317 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/knn/NearestNeighbour.cc0000664000175000017500000000777015263665111027230 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/knn/GridBox.cc0000664000175000017500000001567515263665111025325 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/knn/KNearestNeighboursBase.h0000664000175000017500000000177315263665111030160 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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(const FunctionSpace&); bool extractTreeFromCache(const Cache&); util::IndexKDTree pTree_; }; } // namespace method } // namespace interpolation } // namespace atlas ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/knn/GridBoxMethod.cc0000664000175000017500000001506315263665111026455 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/knn/GridBoxMaximum.cc0000664000175000017500000000670115263665111026651 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/knn/GridBox.h0000664000175000017500000000404715263665111025156 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/knn/KNearestNeighboursBase.cc0000664000175000017500000000704015263665111030307 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 { 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/knn/KNearestNeighbours.h0000664000175000017500000000327015263665111027357 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/PointSet.h0000664000175000017500000001152615263665111024577 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/MethodFactory.cc0000664000175000017500000000471215263665111025737 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/PointIndex2.cc0000664000175000017500000000363615263665111025336 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/Ray.cc0000664000175000017500000000257415263665111023726 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/binning/0000775000175000017500000000000015263665111024300 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/interpolation/method/binning/Binning.cc0000664000175000017500000002260615263665111026201 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/binning/Binning.h0000664000175000017500000000541515263665111026042 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/Ray.h0000664000175000017500000000253415263665111023564 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/unstructured/0000775000175000017500000000000015263665111025423 5ustar alastairalastair././@LongLink0000644000000000000000000000015500000000000011604 Lustar rootrootecmwf-atlas-b0f79ee/src/atlas/interpolation/method/unstructured/ConservativeSphericalPolygonInterpolation.ccecmwf-atlas-b0f79ee/src/atlas/interpolation/method/unstructured/ConservativeSphericalPolygonInterpol0000664000175000017500000033136015263665111034744 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_areas_minus_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_areas_minus_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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/unstructured/UnstructuredBilinearLonLat.cc0000664000175000017500000004461415263665111033232 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/unstructured/SphericalMeanValue.h0000664000175000017500000000566415263665111031317 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ././@LongLink0000644000000000000000000000015400000000000011603 Lustar rootrootecmwf-atlas-b0f79ee/src/atlas/interpolation/method/unstructured/ConservativeSphericalPolygonInterpolation.hecmwf-atlas-b0f79ee/src/atlas/interpolation/method/unstructured/ConservativeSphericalPolygonInterpol0000664000175000017500000003134015263665111034737 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/unstructured/SphericalMeanValue.cc0000664000175000017500000003746015263665111031454 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/unstructured/FiniteElement.h0000664000175000017500000000552115263665111030327 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/unstructured/UnstructuredBilinearLonLat.h0000664000175000017500000000504215263665111033064 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/unstructured/FiniteElement.cc0000664000175000017500000005457715263665111030504 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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/Geometry.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; geometry::Earth earth; for (idx_t n = 0; n < lonlat.shape(0); ++n) { const PointLonLat p1(lonlat(n, 0), lonlat(n, 1)); earth.lonlat2xyz(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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/PointIndex3.h0000664000175000017500000000500315263665111025167 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/sphericalvector/0000775000175000017500000000000015263665111026051 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/interpolation/method/sphericalvector/SphericalVector.h0000664000175000017500000000702715263665111031325 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/sphericalvector/SparseMatrix.h0000664000175000017500000000604315263665111030647 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/sphericalvector/Types.h0000664000175000017500000000144015263665111027325 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/sphericalvector/SphericalVector.cc0000664000175000017500000002235015263665111031457 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/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; } haloExchange(sourceField); if (complexWeights_.nonZeros() > 0) { Method::check_compatibility(sourceField, targetField, matrix()); 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; } ATLAS_ASSERT(adjoint_, "\"adjoint\" needs to be set to \"true\" in Config."); if (complexWeightsAdjoint_.nonZeros() > 0) { Method::check_compatibility(sourceField, targetField, matrix()); interpolate_vector_field(targetField, sourceField, WeightsMatMulAdjoint(complexWeightsAdjoint_, realWeightsAdjoint_)); } sourceField.set_dirty(); 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/sphericalvector/ComplexMatrixMultiply.h0000664000175000017500000002100415263665111032553 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/Method.h0000664000175000017500000001534215263665111024252 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/PointIndex2.h0000664000175000017500000000426615263665111025200 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/PointIndex3.cc0000664000175000017500000000446615263665111025341 0ustar alastairalastair /* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/method/MethodFactory.h0000664000175000017500000000244415263665111025601 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/AssembleGlobalMatrix.h0000664000175000017500000000177015263665111025613 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/Cache.h0000664000175000017500000001152515263665111022554 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/Vector2D.cc0000664000175000017500000000121615263665111023333 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/element/0000775000175000017500000000000015263665111023025 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/interpolation/element/Triag2D.h0000664000175000017500000000422115263665111024431 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/element/Triag2D.cc0000664000175000017500000000617415263665111024600 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/element/Quad2D.h0000664000175000017500000000507315263665111024263 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/element/Quad3D.h0000664000175000017500000000420515263665111024260 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/element/Quad3D.cc0000664000175000017500000000557115263665111024425 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/element/Triag3D.cc0000664000175000017500000000637215263665111024601 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/element/Quad2D.cc0000664000175000017500000001422715263665111024422 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation/element/Triag3D.h0000664000175000017500000000431715263665111024440 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/option.h0000664000175000017500000000077515263665111020177 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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" ecmwf-atlas-b0f79ee/src/atlas/atlas.h0000664000175000017500000000010715263665111017760 0ustar alastairalastair/// @namespace atlas /// @brief Contains all atlas classes and methods ecmwf-atlas-b0f79ee/src/atlas/domain/0000775000175000017500000000000015263665111017754 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/domain/detail/0000775000175000017500000000000015263665111021216 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/domain/detail/ZonalBandDomain.h0000664000175000017500000000365215263665111024375 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/domain/detail/Domain.cc0000664000175000017500000000434215263665111022737 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/domain/detail/RectangularDomain.cc0000664000175000017500000001117415263665111025130 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/domain/detail/DomainFactory.h0000664000175000017500000000301315263665111024123 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/domain/detail/DomainFactory.cc0000664000175000017500000000330715263665111024267 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/domain/detail/EmptyDomain.h0000664000175000017500000000271115263665111023616 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/domain/detail/Domain.h0000664000175000017500000000506115263665111022600 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/domain/detail/GlobalDomain.h0000664000175000017500000000301415263665111023715 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/domain/detail/RectangularDomain.h0000664000175000017500000000601615263665111024771 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/domain/detail/ZonalBandDomain.cc0000664000175000017500000000646115263665111024534 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/domain/detail/GlobalDomain.cc0000664000175000017500000000304415263665111024056 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/domain/detail/EmptyDomain.cc0000664000175000017500000000226115263665111023754 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/domain/Domain.cc0000664000175000017500000001020615263665111021471 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/domain/Domain.h0000664000175000017500000001226615263665111021343 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/interpolation.h0000664000175000017500000000070515263665111021547 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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" ecmwf-atlas-b0f79ee/src/atlas/redistribution/0000775000175000017500000000000015263665111021553 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/redistribution/detail/0000775000175000017500000000000015263665111023015 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/redistribution/detail/RedistributionInterface.cc0000664000175000017500000000403615263665111030156 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/redistribution/detail/RedistributeStructuredColumns.h0000664000175000017500000001014715263665111031264 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 ecmwf-atlas-b0f79ee/src/atlas/redistribution/detail/RedistributionInterface.h0000664000175000017500000000262315263665111030020 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the 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 ecmwf-atlas-b0f79ee/src/atlas/redistribution/detail/RedistributeGeneric.h0000664000175000017500000000304015263665111027125 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 ecmwf-atlas-b0f79ee/src/atlas/redistribution/detail/RedistributionImpl.cc0000664000175000017500000000131715263665111027156 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 ecmwf-atlas-b0f79ee/src/atlas/redistribution/detail/RedistributeGeneric.cc0000664000175000017500000004056015263665111027273 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 ecmwf-atlas-b0f79ee/src/atlas/redistribution/detail/RedistributionImplFactory.h0000664000175000017500000000255215263665111030352 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 ecmwf-atlas-b0f79ee/src/atlas/redistribution/detail/RedistributionImpl.h0000664000175000017500000000343715263665111027025 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 ecmwf-atlas-b0f79ee/src/atlas/redistribution/detail/RedistributionImplFactory.cc0000664000175000017500000000264515263665111030513 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 ecmwf-atlas-b0f79ee/src/atlas/redistribution/detail/RedistributeStructuredColumns.cc0000664000175000017500000003174715263665111031433 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 ecmwf-atlas-b0f79ee/src/atlas/redistribution/Redistribution.cc0000664000175000017500000000315515263665111025074 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 ecmwf-atlas-b0f79ee/src/atlas/redistribution/Redistribution.h0000664000175000017500000000471715263665111024743 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 ecmwf-atlas-b0f79ee/src/atlas/CMakeLists.txt0000664000175000017500000007634715263665111021266 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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/GridPointFlags.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/FieldBuilder.cc field/FieldBuilder.h field/builders/GridBoxArea.cc field/builders/GridBoxArea.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 ) ecmwf-atlas-b0f79ee/src/atlas/grid.h0000664000175000017500000000133015263665111017600 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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" ecmwf-atlas-b0f79ee/src/atlas/mesh/0000775000175000017500000000000015263665111017441 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/mesh/Connectivity.cc0000664000175000017500000007374415263665111022445 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/detail/0000775000175000017500000000000015263665111020703 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/mesh/detail/MeshIntf.h0000664000175000017500000000366315263665111022601 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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(); Mesh::Implementation* atlas__Mesh__new_grid(Grid::Implementation* grid, const util::Config* config); Mesh::Implementation* atlas__Mesh__new_grid_distribution(Grid::Implementation* grid, grid::Distribution::Implementation* distribution, const util::Config* config); Mesh::Implementation* atlas__Mesh__new_grid_partitioner(Grid::Implementation* grid, grid::Partitioner::Implementation* partitioner, const util::Config* config); 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/detail/PartitionGraph.cc0000664000175000017500000001361415263665111024152 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/detail/MeshImpl.cc0000664000175000017500000001355215263665111022736 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/detail/MeshImpl.h0000664000175000017500000001306515263665111022577 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/detail/PartitionGraph.h0000664000175000017500000000332115263665111024006 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/detail/MeshBuilderIntf.cc0000664000175000017500000000421715263665111024242 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/detail/AccumulateFacets.cc0000664000175000017500000003220015263665111024420 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/detail/MeshBuilderIntf.h0000664000175000017500000000254115263665111024102 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/detail/MeshIntf.cc0000664000175000017500000000747515263665111022744 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/grid/Distribution.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/detail/grid/Grid.h" #include "atlas/mesh/detail/MeshIntf.h" #include "atlas/mesh/Nodes.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" namespace atlas { namespace mesh { //---------------------------------------------------------------------------------------------------------------------- // C wrapper interfaces to C++ routines Mesh::Implementation* atlas__Mesh__new() { return new Mesh::Implementation(); } Mesh::Implementation* atlas__Mesh__new_grid(Grid::Implementation* grid, const util::Config* config) { ATLAS_ASSERT(config != nullptr, "Cannot access uninitialised atlas_Config"); Mesh::Implementation* mesh; { Mesh m{Grid{grid}, *config}; mesh = m.get(); mesh->attach(); } mesh->detach(); return mesh; } Mesh::Implementation* atlas__Mesh__new_grid_distribution(Grid::Implementation* grid, grid::Distribution::Implementation* distribution, const util::Config* config) { ATLAS_ASSERT(config != nullptr, "Cannot access uninitialised atlas_Config"); Mesh::Implementation* mesh; { Mesh m{Grid{grid}, grid::Distribution{distribution}, *config}; mesh = m.get(); mesh->attach(); } mesh->detach(); return mesh; } Mesh::Implementation* atlas__Mesh__new_grid_partitioner(Grid::Implementation* grid, grid::Partitioner::Implementation* partitioner, const util::Config* config) { ATLAS_ASSERT(config != nullptr, "Cannot access uninitialised atlas_Config"); Mesh::Implementation* mesh; { Mesh m{Grid{grid}, grid::Partitioner{partitioner}, *config}; mesh = m.get(); mesh->attach(); } mesh->detach(); return mesh; } 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/detail/AccumulateFacets.h0000664000175000017500000000316515263665111024272 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/MeshBuilder.h0000664000175000017500000002373015263665111022022 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/PartitionPolygon.h0000664000175000017500000000363315263665111023140 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/IsGhostNode.h0000664000175000017500000000202115263665111021773 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/elementtypes/0000775000175000017500000000000015263665111022157 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/mesh/elementtypes/Line.h0000664000175000017500000000307215263665111023221 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/elementtypes/Quadrilateral.h0000664000175000017500000000330215263665111025120 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/elementtypes/Triangle.h0000664000175000017500000000323515263665111024100 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/elementtypes/Classification.h0000664000175000017500000000373515263665111025273 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/elementtypes/Pentagon.h0000664000175000017500000000310615263665111024103 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/Halo.cc0000664000175000017500000000154215263665111020635 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/Connectivity.cu0000664000175000017500000000003315263665111022444 0ustar alastairalastair#include "Connectivity.cc" ecmwf-atlas-b0f79ee/src/atlas/mesh/HybridElements.cc0000664000175000017500000003433315263665111022674 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/HybridElements.h0000664000175000017500000002424715263665111022541 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/Nodes.cc0000664000175000017500000002327215263665111021026 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/Elements.cc0000664000175000017500000002216315263665111021530 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/Halo.h0000664000175000017500000000161015263665111020473 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/Mesh.cc0000664000175000017500000000557115263665111020654 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/Nodes.h0000664000175000017500000001405515263665111020667 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/PartitionPolygon.cc0000664000175000017500000003066615263665111023304 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/Connectivity.h0000664000175000017500000006036215263665111022277 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/ElementType.cc0000664000175000017500000000434015263665111022204 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/Elements.h0000664000175000017500000002074415263665111021375 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/Mesh.h0000664000175000017500000001200315263665111020502 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/0000775000175000017500000000000015263665111021101 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildXYZField.cc0000664000175000017500000000410115263665111024022 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/Geometry.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; geometry::Earth earth; for (idx_t n = 0; n < nodes.size(); ++n) { const PointLonLat p1(lonlat(n, 0), lonlat(n, 1)); earth.lonlat2xyz(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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/WriteLoadBalanceReport.h0000664000175000017500000000171315263665111025610 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildStatistics.h0000664000175000017500000000150015263665111024360 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/GetCubedSphereNodalArea.cc0000664000175000017500000000351415263665111026013 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildHalo.cc0000664000175000017500000015623315263665111023265 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildHalo.h0000664000175000017500000000262615263665111023123 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildConvexHull3D.h0000664000175000017500000000152715263665111024515 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/Reorder.h0000664000175000017500000000733115263665111022660 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/ReorderReverseCuthillMckee.cc0000664000175000017500000001345215263665111026645 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/Build2DCellCentres.cc0000664000175000017500000001152615263665111024766 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildDualMesh.h0000664000175000017500000000361415263665111023740 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildNode2CellConnectivity.cc0000664000175000017500000000776015263665111026550 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildTorusXYZField.h0000664000175000017500000000262115263665111024726 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/Build2DCellCentres.h0000664000175000017500000000152015263665111024621 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/ExtendNodesGlobal.cc0000664000175000017500000000675415263665111024765 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildNode2CellConnectivity.h0000664000175000017500000000230415263665111026377 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the 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); } // ------------------------------------------------------------------ ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildEdges.cc0000664000175000017500000007014715263665111023430 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/mesh/actions/BuildXYZField.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); std::string node_order = "xy"; config.get("node_order", node_order); ATLAS_ASSERT(node_order == "xy" || node_order == "global_index"); std::string cell_order = "xy"; config.get("cell_order", cell_order); ATLAS_ASSERT(cell_order == "xy" || cell_order == "left_first" || cell_order == "right_first"); 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); BuildXYZField("xyz")(nodes); const auto node_gidx = array::make_view(nodes.global_index()); const auto node_lonlat = array::make_view(nodes.lonlat()); const auto node_xyz = array::make_view(nodes.field("xyz")); 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 (node_order == "global_index") { if (node_gidx(ip1) > node_gidx(ip2)) { idx_t swapped[2] = {ip2, ip1}; edge_nodes.set(edge, swapped); } } else { 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 (cell_order == "xy") { if (e2 == cell_nodes.missing_value()) { continue; } 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; } } else { bool left_cell_first = (cell_order == "left_first"); const idx_t node1 = edge_nodes(edge, 0); const idx_t node2 = edge_nodes(edge, 1); const double edge_normal_x = node_xyz(node1, YY) * node_xyz(node2, ZZ) - node_xyz(node1, ZZ) * node_xyz(node2, YY); const double edge_normal_y = node_xyz(node1, ZZ) * node_xyz(node2, XX) - node_xyz(node1, XX) * node_xyz(node2, ZZ); const double edge_normal_z = node_xyz(node1, XX) * node_xyz(node2, YY) - node_xyz(node1, YY) * node_xyz(node2, XX); const double edge_length = std::sqrt(edge_normal_x * edge_normal_x + edge_normal_y * edge_normal_y + edge_normal_z * edge_normal_z); double centre_x = 0.; double centre_y = 0.; double centre_z = 0.; for (idx_t jnode = 0; jnode < cell_nodes.cols(e1); ++jnode) { const idx_t cell_node = cell_nodes(e1, jnode); centre_x += node_xyz(cell_node, XX); centre_y += node_xyz(cell_node, YY); centre_z += node_xyz(cell_node, ZZ); } bool e1_is_right = edge_normal_x * centre_x + edge_normal_y * centre_y + edge_normal_z * centre_z < 0.; if (edge_length == 0.) { const double longitude1 = node_lonlat(node1, LON); const double longitude2 = node_lonlat(node2, LON); const bool north_pole = node_lonlat(node1, LAT) > 0.; e1_is_right = north_pole ? longitude1 > longitude2 : longitude1 < longitude2; } if (e1_is_right) { if (e2 == cell_nodes.missing_value()) { // Re-orient the edge such that e1 is on its left side. idx_t swapped[2] = {node2, node1}; edge_nodes.set(edge, swapped); } else { edge_to_elem_data[iedge * 2 + 0] = (left_cell_first ? e2 : e1); edge_to_elem_data[iedge * 2 + 1] = (left_cell_first ? e1 : e2); } } } } 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_edges_config(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); build_edges(m, *config); } 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildParallelFields.cc0000664000175000017500000013045015263665111025256 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/ExtendNodesGlobal.h0000664000175000017500000000165215263665111024617 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildPeriodicBoundaries.cc0000664000175000017500000002113015263665111026137 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildConvexHull3D.cc0000664000175000017500000001155215263665111024652 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildStatistics.cc0000664000175000017500000002342015263665111024523 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildXYZField.h0000664000175000017500000000233515263665111023673 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildTorusXYZField.cc0000664000175000017500000000533515263665111025071 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/WriteLoadBalanceReport.cc0000664000175000017500000002273015263665111025750 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildPeriodicBoundaries.h0000664000175000017500000000213515263665111026005 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildDualMesh.cc0000664000175000017500000004434415263665111024103 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildCellCentres.h0000664000175000017500000000201115263665111024427 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/GetCubedSphereNodalArea.h0000664000175000017500000000111315263665111025646 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildParallelFields.h0000664000175000017500000000457715263665111025132 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildCellCentres.cc0000664000175000017500000001172515263665111024601 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/ReorderHilbert.h0000664000175000017500000000332415263665111024170 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/BuildEdges.h0000664000175000017500000000314415263665111023263 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the 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 namespace eckit { class Configuration; } // C wrapper interfaces to C++ routines extern "C" { void atlas__build_edges(atlas::mesh::detail::MeshImpl* mesh); void atlas__build_edges_config(atlas::mesh::detail::MeshImpl* mesh, const eckit::Configuration* config); void atlas__build_pole_edges(atlas::mesh::detail::MeshImpl* mesh); void atlas__build_node_to_edge_connectivity(atlas::mesh::detail::MeshImpl* mesh); } // ------------------------------------------------------------------ ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/ReorderHilbert.cc0000664000175000017500000003456615263665111024342 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/Reorder.cc0000664000175000017500000002144615263665111023021 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/actions/ReorderReverseCuthillMckee.h0000664000175000017500000000302615263665111026503 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/mesh/ElementType.h0000664000175000017500000000413215263665111022045 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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" ecmwf-atlas-b0f79ee/src/atlas/mesh/MeshBuilder.cc0000664000175000017500000004575615263665111022174 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 ecmwf-atlas-b0f79ee/src/atlas/projection/0000775000175000017500000000000015263665111020661 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/projection/Jacobian.h0000664000175000017500000001011515263665111022536 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/0000775000175000017500000000000015263665111022123 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/projection/detail/CubedSphereEquiAnglProjection.h0000664000175000017500000000442415263665111030154 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/ProjProjection.h0000664000175000017500000000322015263665111025240 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/CubedSphereProjectionBase.h0000664000175000017500000001020515263665111027313 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/LambertConformalConicProjection.h0000664000175000017500000000405015263665111030533 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/ProjProjection.cc0000664000175000017500000001530415263665111025404 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/CubedSphereEquiAnglProjection.cc0000664000175000017500000003147515263665111030320 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/SchmidtProjection.cc0000664000175000017500000001122215263665111026060 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/ProjectionImpl.h0000664000175000017500000001447715263665111025247 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/CubedSphereEquiDistProjection.cc0000664000175000017500000001350115263665111030330 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/ProjectionFactory.cc0000664000175000017500000000541215263665111026100 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/ProjectionUtilities.h0000664000175000017500000001072315263665111026307 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/ProjectionImpl.cc0000664000175000017500000003401215263665111025370 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/LonLatProjection.h0000664000175000017500000000373015263665111025525 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/LonLatProjection.cc0000664000175000017500000001572115263665111025666 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/MercatorProjection.h0000664000175000017500000000446715263665111026120 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/SchmidtProjection.h0000664000175000017500000000344715263665111025734 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/CubedSphereProjectionBase.cc0000664000175000017500000002044715263665111027462 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/MercatorProjection.cc0000664000175000017500000001556615263665111026260 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/CubedSphereEquiDistProjection.h0000664000175000017500000000435515263665111030201 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/VariableResolutionProjection.h0000664000175000017500000000656315263665111030154 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/LambertAzimuthalEqualAreaProjection.h0000664000175000017500000000353715263665111031367 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/VariableResolutionProjection.cc0000664000175000017500000004734215263665111030312 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/LambertAzimuthalEqualAreaProjection.cc0000664000175000017500000000767415263665111031533 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/LambertConformalConicProjection.cc0000664000175000017500000001161715263665111030700 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 ecmwf-atlas-b0f79ee/src/atlas/projection/detail/ProjectionFactory.h0000664000175000017500000000324615263665111025745 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/projection/Projection.cc0000664000175000017500000000431015263665111023302 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/projection/Projection.h0000664000175000017500000000452415263665111023153 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/mesh.h0000664000175000017500000000130115263665111017605 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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" ecmwf-atlas-b0f79ee/src/atlas/field.h0000664000175000017500000000101715263665111017740 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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" ecmwf-atlas-b0f79ee/src/atlas/io/0000775000175000017500000000000015263665111017114 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/io/VectorAdaptor.h0000664000175000017500000000321215263665111022040 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 ecmwf-atlas-b0f79ee/src/atlas/io/ArrayAdaptor.cc0000664000175000017500000000366415263665111022025 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 ecmwf-atlas-b0f79ee/src/atlas/io/ArrayAdaptor.h0000664000175000017500000000177415263665111021667 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 ecmwf-atlas-b0f79ee/src/atlas/io/atlas-io.h0000664000175000017500000000077515263665111021007 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" ecmwf-atlas-b0f79ee/src/atlas/functionspace.h0000664000175000017500000000144715263665111021525 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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" ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/0000775000175000017500000000000015263665111021350 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/0000775000175000017500000000000015263665111022612 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/StructuredMeshGenerator.h0000664000175000017500000000430115263665111027611 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/MeshGeneratorFactory.cc0000664000175000017500000000442415263665111027220 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/RegularMeshGenerator.cc0000664000175000017500000005501115263665111027210 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/CubedSphereDualMeshGenerator.h0000664000175000017500000000335515263665111030454 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/cubedsphere/0000775000175000017500000000000015263665111025103 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/cubedsphere/CubedSphereUtility.h0000664000175000017500000001237415263665111031040 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/cubedsphere/CubedSphereUtility.cc0000664000175000017500000002370015263665111031171 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/CubedSphereMeshGenerator.h0000664000175000017500000000334015263665111027640 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/HealpixMeshGenerator.cc0000664000175000017500000012465615263665111027215 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/MeshGeneratorFactory.h0000664000175000017500000000320015263665111027051 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/MeshGeneratorImpl.cc0000664000175000017500000001004615263665111026507 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/HealpixMeshGenerator.h0000664000175000017500000000463715263665111027053 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/DelaunayMeshGenerator.cc0000664000175000017500000003606715263665111027363 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/CubedSphereDualMeshGenerator.cc0000664000175000017500000004621215263665111030611 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/RegularMeshGenerator.h0000664000175000017500000000350615263665111027054 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/DelaunayMeshGenerator.h0000664000175000017500000000307315263665111027214 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/NodalCubedSphereMeshGenerator.cc0000664000175000017500000003320215263665111030754 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc0000664000175000017500000017477715263665111030000 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 (options.getBool("patch_pole")) { const bool north_to_south = rg.y().front() > rg.y().back(); const idx_t north = north_to_south ? 0 : rg.ny() - 1; const idx_t south = north_to_south ? rg.ny() - 1 : 0; auto extend_patch_region = [&](idx_t jlat) { if (rg.nx(jlat) > 0 && distribution.partition(rg.index(0, jlat)) == mypart) { const idx_t previous_size = region.lat_end.at(jlat) - region.lat_begin.at(jlat) + 1; const idx_t patch_end = rg.nx(jlat) - ((rg.periodic() && !options.getBool("three_dimensional")) ? 0 : 1); region.lat_begin.at(jlat) = 0; region.lat_end.at(jlat) = patch_end; region.nnodes += patch_end + 1 - previous_size; } }; if (rg.y(north) != 90. && rg.domain().containsNorthPole()) { extend_patch_region(north); } if (rg.y(south) != -90. && rg.domain().containsSouthPole()) { extend_patch_region(south); } } 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(); const int part_north = distribution.partition(y_numbering < 0 ? 0 : rg.size() - nx_north); const int part_south = distribution.partition(y_numbering < 0 ? rg.size() - nx_south : 0); bool mypart_at_north = part_north == mypart; bool mypart_at_south = part_south == 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; auto count_pole_patch_cells = [&](idx_t nb_ring_nodes) { ATLAS_ASSERT(nb_ring_nodes >= 3); if (patch_quads) { // A polygon with N rim vertices needs N-2 triangle-equivalents. // Each quad consumes two; an odd rim leaves one triangle. nquads += (nb_ring_nodes - 2) / 2; ntriags += nb_ring_nodes % 2; } else { ntriags += nb_ring_nodes - 2; } }; if (include_north_pole) { ++nnodes; ntriags += nx_north - (periodic_east_west ? 0 : 1); } else if (patch_north_pole) { count_pole_patch_cells(nx_north); } if (include_south_pole) { ++nnodes; ntriags += nx_south - (periodic_east_west ? 0 : 1); } else if (patch_south_pole) { count_pole_patch_cells(nx_south); } 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; }; auto node_on_latitude = [&](idx_t ilat, idx_t ip) { return node_numbering.at(offset_loc.at(ilat) + ip); }; auto set_quad_connectivity = [&](idx_t cell, const idx_t nodes[]) { ATLAS_ASSERT(node_connectivity.cols(cell) == 4); for (idx_t node = 0; node < 4; ++node) { node_connectivity.set(cell, node, nodes[node]); } }; auto set_triag_connectivity = [&](idx_t cell, const idx_t nodes[]) { ATLAS_ASSERT(node_connectivity.cols(cell) == 3); for (idx_t node = 0; node < 3; ++node) { node_connectivity.set(cell, node, nodes[node]); } }; auto add_patch_quad = [&]() { ATLAS_ASSERT(jquad < nquads); jcell = quad_begin + jquad++; set_quad_connectivity(jcell, quad_nodes); cells_glb_idx(jcell) = jcell + 1; cells_part(jcell) = mypart; Topology::set(cells_flags(jcell), Topology::PATCH); }; auto add_patch_triag = [&]() { ATLAS_ASSERT(jtriag < ntriags); jcell = triag_begin + jtriag++; set_triag_connectivity(jcell, triag_nodes); cells_glb_idx(jcell) = jcell + 1; cells_part(jcell) = mypart; Topology::set(cells_flags(jcell), Topology::PATCH); }; enum class Pole { NORTH, SOUTH }; auto add_quad_pole_patch = [&](idx_t ilat, idx_t nb_ring_nodes, Pole pole) { ATLAS_ASSERT(nb_ring_nodes >= 3); idx_t first = 0; idx_t last = nb_ring_nodes - 1; // Work inward from both sides of the rim. Four remaining vertices form // a quad; three remaining vertices form the final triangle. while (first + 3 <= last) { if (pole == Pole::NORTH) { quad_nodes[0] = node_on_latitude(ilat, first); quad_nodes[1] = node_on_latitude(ilat, first + 1); quad_nodes[2] = node_on_latitude(ilat, last - 1); quad_nodes[3] = node_on_latitude(ilat, last); } else { quad_nodes[0] = node_on_latitude(ilat, last); quad_nodes[1] = node_on_latitude(ilat, last - 1); quad_nodes[2] = node_on_latitude(ilat, first + 1); quad_nodes[3] = node_on_latitude(ilat, first); } add_patch_quad(); ++first; --last; } if (first + 2 == last) { if (pole == Pole::NORTH) { triag_nodes[0] = node_on_latitude(ilat, first); triag_nodes[1] = node_on_latitude(ilat, first + 1); triag_nodes[2] = node_on_latitude(ilat, last); } else { triag_nodes[0] = node_on_latitude(ilat, last); triag_nodes[1] = node_on_latitude(ilat, first + 1); triag_nodes[2] = node_on_latitude(ilat, first); } add_patch_triag(); } }; 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++; set_quad_connectivity(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++; set_triag_connectivity(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); } set_triag_connectivity(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) { add_quad_pole_patch(ilat, rg.nx(jlat), Pole::NORTH); } 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++; set_triag_connectivity(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); } set_triag_connectivity(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) { add_quad_pole_patch(ilat, rg.nx(jlat), Pole::SOUTH); } 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++; set_triag_connectivity(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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/MeshGeneratorInterface.cc0000664000175000017500000001003115263665111027500 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/MeshGeneratorImpl.h0000664000175000017500000000431715263665111026355 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/MeshGeneratorInterface.h0000664000175000017500000000515615263665111027356 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/NodalCubedSphereMeshGenerator.h0000664000175000017500000000311715263665111030620 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/detail/CubedSphereMeshGenerator.cc0000664000175000017500000011566415263665111030013 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/MeshGenerator.cc0000664000175000017500000000441115263665111024422 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator/MeshGenerator.h0000664000175000017500000000425015263665111024265 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/field/0000775000175000017500000000000015263665111017570 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/field/FieldCreatorArraySpec.cc0000664000175000017500000000501215263665111024252 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/field/detail/0000775000175000017500000000000015263665111021032 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/field/detail/FieldImpl.cc0000664000175000017500000001555315263665111023217 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 << "Field[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().json(eckit::JSON::Formatting::COMPACT) << ",functionspace=" << (functionspace_ ? functionspace_->type() : "null"); 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 ecmwf-atlas-b0f79ee/src/atlas/field/detail/FieldInterface.h0000664000175000017500000001117215263665111024051 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/field/detail/MissingValue.cc0000664000175000017500000001201415263665111023745 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 ecmwf-atlas-b0f79ee/src/atlas/field/detail/FieldImpl.h0000664000175000017500000002523115263665111023053 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/field/detail/MultiFieldInterface.h0000664000175000017500000000256115263665111025066 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/field/detail/MultiFieldImpl.cc0000664000175000017500000000171015263665111024220 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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_); } } } ecmwf-atlas-b0f79ee/src/atlas/field/detail/MissingValue.h0000664000175000017500000000413615263665111023615 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 ecmwf-atlas-b0f79ee/src/atlas/field/detail/MultiFieldInterface.cc0000664000175000017500000000443615263665111025227 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/field/detail/MultiFieldImpl.h0000664000175000017500000000546015263665111024070 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/field/detail/for_each.h0000664000175000017500000002640015263665111022753 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/field/detail/FieldInterface.cc0000664000175000017500000002662615263665111024221 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/field/FieldSet.h0000664000175000017500000004231315263665111021443 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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__shapef(FieldSetImpl* This, char* name, int*& shape, int& rank); void atlas__FieldSet__shapef_by_idx(FieldSetImpl* This, int& idx, int*& shape, int& rank); 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__device_data_int_specf(FieldSetImpl* This, char* name, int*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__FieldSet__device_data_long_specf(FieldSetImpl* This, char* name, long*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__FieldSet__device_data_float_specf(FieldSetImpl* This, char* name, float*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__FieldSet__device_data_double_specf(FieldSetImpl* This, char* name, double*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__FieldSet__device_data_int_specf_by_idx(FieldSetImpl* This, int& idx, int*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__FieldSet__device_data_long_specf_by_idx(FieldSetImpl* This, int& idx, long*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__FieldSet__device_data_float_specf_by_idx(FieldSetImpl* This, int& idx, float*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__FieldSet__device_data_double_specf_by_idx(FieldSetImpl* This, int& idx, double*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__FieldSet__set_host_needs_update(FieldSetImpl* This, int value); void atlas__FieldSet__set_host_needs_update_name(FieldSetImpl* This, char* name, int value); void atlas__FieldSet__set_host_needs_update_by_idx(FieldSetImpl* This, int& idx, int value); void atlas__FieldSet__set_device_needs_update(FieldSetImpl* This, int value); void atlas__FieldSet__set_device_needs_update_name(FieldSetImpl* This, char* name, int value); void atlas__FieldSet__set_device_needs_update_by_idx(FieldSetImpl* This, int& idx, int value); void atlas__FieldSet__sync_host_device(FieldSetImpl* This); void atlas__FieldSet__sync_host_device_name(FieldSetImpl* This, char* name); void atlas__FieldSet__sync_host_device_by_idx(FieldSetImpl* This, int& idx); void atlas__FieldSet__sync_host(FieldSetImpl* This); void atlas__FieldSet__sync_host_name(FieldSetImpl* This, char* name); void atlas__FieldSet__sync_host_by_idx(FieldSetImpl* This, int& idx); void atlas__FieldSet__sync_device(FieldSetImpl* This); void atlas__FieldSet__sync_device_name(FieldSetImpl* This, char* name); void atlas__FieldSet__sync_device_by_idx(FieldSetImpl* This, int& idx); void atlas__FieldSet__allocate_device(FieldSetImpl* This); void atlas__FieldSet__allocate_device_name(FieldSetImpl* This, char* name); void atlas__FieldSet__allocate_device_by_idx(FieldSetImpl* This, int& idx); void atlas__FieldSet__update_device(FieldSetImpl* This); void atlas__FieldSet__update_device_name(FieldSetImpl* This, char* name); void atlas__FieldSet__update_device_by_idx(FieldSetImpl* This, int& idx); void atlas__FieldSet__update_host(FieldSetImpl* This); void atlas__FieldSet__update_host_name(FieldSetImpl* This, char* name); void atlas__FieldSet__update_host_by_idx(FieldSetImpl* This, int& idx); void atlas__FieldSet__deallocate_device(FieldSetImpl* This); void atlas__FieldSet__deallocate_device_name(FieldSetImpl* This, char* name); void atlas__FieldSet__deallocate_device_by_idx(FieldSetImpl* This, int& idx); 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 ecmwf-atlas-b0f79ee/src/atlas/field/MultiField.h0000664000175000017500000000655615263665111022013 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/field/MissingValue.cc0000664000175000017500000000624215263665111022511 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 ecmwf-atlas-b0f79ee/src/atlas/field/MultiField.cc0000664000175000017500000000572015263665111022141 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/field/Field.cc0000664000175000017500000002010215263665111021115 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/field/Halo.cc0000664000175000017500000000261115263665111020762 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 ecmwf-atlas-b0f79ee/src/atlas/field/FieldSet.cc0000664000175000017500000005311415263665111021602 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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__shapef(FieldSetImpl* This, char* name, int*& shape, int& rank) { atlas__Field__shapef(This->field(name).get(), shape, rank); } void atlas__FieldSet__shapef_by_idx(FieldSetImpl* This, int& idx, int*& shape, int& rank) { atlas__Field__shapef(This->operator[](idx).get(), shape, rank); } 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); } void atlas__FieldSet__device_data_int_specf(FieldSetImpl* This, char* name, int*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__device_data_int_specf(This->field(name).get(), data, rank, shapef, stridesf); } void atlas__FieldSet__device_data_long_specf(FieldSetImpl* This, char* name, long*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__device_data_long_specf(This->field(name).get(), data, rank, shapef, stridesf); } void atlas__FieldSet__device_data_float_specf(FieldSetImpl* This, char* name, float*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__device_data_float_specf(This->field(name).get(), data, rank, shapef, stridesf); } void atlas__FieldSet__device_data_double_specf(FieldSetImpl* This, char* name, double*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__device_data_double_specf(This->field(name).get(), data, rank, shapef, stridesf); } void atlas__FieldSet__device_data_int_specf_by_idx(FieldSetImpl* This, int& idx, int*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__device_data_int_specf(This->operator[](idx).get(), data, rank, shapef, stridesf); } void atlas__FieldSet__device_data_long_specf_by_idx(FieldSetImpl* This, int& idx, long*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__device_data_long_specf(This->operator[](idx).get(), data, rank, shapef, stridesf); } void atlas__FieldSet__device_data_float_specf_by_idx(FieldSetImpl* This, int& idx, float*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__device_data_float_specf(This->operator[](idx).get(), data, rank, shapef, stridesf); } void atlas__FieldSet__device_data_double_specf_by_idx(FieldSetImpl* This, int& idx, double*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__device_data_double_specf(This->operator[](idx).get(), data, rank, shapef, stridesf); } void atlas__FieldSet__set_host_needs_update(FieldSetImpl* This, int value) { This->setHostNeedsUpdate(value); } void atlas__FieldSet__set_host_needs_update_name(FieldSetImpl* This, char* name, int value) { This->field(name).setHostNeedsUpdate(value); } void atlas__FieldSet__set_host_needs_update_by_idx(FieldSetImpl* This, int& idx, int value) { This->operator[](idx).setHostNeedsUpdate(value); } void atlas__FieldSet__set_device_needs_update(FieldSetImpl* This, int value) { This->setDeviceNeedsUpdate(value); } void atlas__FieldSet__set_device_needs_update_name(FieldSetImpl* This, char* name, int value) { This->field(name).setDeviceNeedsUpdate(value); } void atlas__FieldSet__set_device_needs_update_by_idx(FieldSetImpl* This, int& idx, int value) { This->operator[](idx).setDeviceNeedsUpdate(value); } void atlas__FieldSet__sync_host_device(FieldSetImpl* This) { for (auto field : *This) { field.syncHostDevice(); } } void atlas__FieldSet__sync_host_device_name(FieldSetImpl* This, char* name) { This->field(name).syncHostDevice(); } void atlas__FieldSet__sync_host_device_by_idx(FieldSetImpl* This, int& idx) { This->operator[](idx).syncHostDevice(); } void atlas__FieldSet__sync_host(FieldSetImpl* This) { This->syncHost(); } void atlas__FieldSet__sync_host_name(FieldSetImpl* This, char* name) { This->field(name).syncHost(); } void atlas__FieldSet__sync_host_by_idx(FieldSetImpl* This, int& idx) { This->operator[](idx).syncHost(); } void atlas__FieldSet__sync_device(FieldSetImpl* This) { This->syncDevice(); } void atlas__FieldSet__sync_device_name(FieldSetImpl* This, char* name) { This->field(name).syncDevice(); } void atlas__FieldSet__sync_device_by_idx(FieldSetImpl* This, int& idx) { This->operator[](idx).syncDevice(); } void atlas__FieldSet__allocate_device(FieldSetImpl* This) { This->allocateDevice(); } void atlas__FieldSet__allocate_device_name(FieldSetImpl* This, char* name) { This->field(name).allocateDevice(); } void atlas__FieldSet__allocate_device_by_idx(FieldSetImpl* This, int& idx) { This->operator[](idx).allocateDevice(); } void atlas__FieldSet__update_device(FieldSetImpl* This) { This->updateDevice(); } void atlas__FieldSet__update_device_name(FieldSetImpl* This, char* name) { This->field(name).updateDevice(); } void atlas__FieldSet__update_device_by_idx(FieldSetImpl* This, int& idx) { This->operator[](idx).updateDevice(); } void atlas__FieldSet__update_host(FieldSetImpl* This) { This->updateHost(); } void atlas__FieldSet__update_host_name(FieldSetImpl* This, char* name) { This->field(name).updateHost(); } void atlas__FieldSet__update_host_by_idx(FieldSetImpl* This, int& idx) { This->operator[](idx).updateHost(); } void atlas__FieldSet__deallocate_device(FieldSetImpl* This) { This->deallocateDevice(); } void atlas__FieldSet__deallocate_device_name(FieldSetImpl* This, char* name) { This->field(name).deallocateDevice(); } void atlas__FieldSet__deallocate_device_by_idx(FieldSetImpl* This, int& idx) { This->operator[](idx).deallocateDevice(); } 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 ecmwf-atlas-b0f79ee/src/atlas/field/FieldBuilder.h0000664000175000017500000000534015263665111022275 0ustar alastairalastair/* * (C) Copyright 2026 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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/Field.h" #include "atlas/util/Config.h" #include "atlas/util/Factory.h" #include "atlas/util/Object.h" #include "atlas/util/ObjectHandle.h" namespace atlas { namespace field { /// Polymorphic implementation interface for operators that construct a Field. /// Implementations retain any source object state needed by the call operator. class AbstractFieldBuilder : public util::Object { public: virtual Field operator()() const = 0; }; /// Registry of named field-builder implementations. /// The configuration `type` selects a factory, which receives a generic Atlas object handle and the complete configuration. class FieldBuilderFactory : public util::Factory { public: static std::string className() { return "FieldBuilderFactory"; } static const AbstractFieldBuilder* build(const util::Config& config, const util::ObjectHandleBase& object); using Factory::Factory; private: virtual const AbstractFieldBuilder* make(const util::Config&, const util::ObjectHandleBase&) = 0; }; /// Factory adapter that self-registers a concrete field-builder implementation. /// ConcreteFieldBuilder must accept `(const util::Config&, const util::ObjectHandleBase&)`. template class FieldBuilderFactoryBuilder : public FieldBuilderFactory { public: using FieldBuilderFactory::FieldBuilderFactory; private: const AbstractFieldBuilder* make(const util::Config& config, const util::ObjectHandleBase& object) override { return new ConcreteFieldBuilder(config, object); } }; /// Owning public handle for a configured field-building operator. /// Construction resolves the implementation through FieldBuilderFactory; invocation returns the newly built Field. class FieldBuilder : public util::ObjectHandle { public: using Handle::Handle; FieldBuilder(const util::Config& config, const util::ObjectHandleBase& object): Handle(FieldBuilderFactory::build(config, object)) {} FieldBuilder(const std::string& type, const util::ObjectHandleBase& object, const util::Config& config = util::NoConfig()): Handle(FieldBuilderFactory::build(util::Config("type", type) | config, object)) {} Field operator()() const { return get()->operator()(); } }; } // namespace field } // namespace atlasecmwf-atlas-b0f79ee/src/atlas/field/Halo.h0000664000175000017500000000162015263665111020623 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 ecmwf-atlas-b0f79ee/src/atlas/field/MultiFieldCreatorIFS.cc0000664000175000017500000002407015263665111024022 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/field/FieldCreatorArraySpec.h0000664000175000017500000000272715263665111024126 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/field/FieldCreatorIFS.h0000664000175000017500000000322515263665111022650 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/field/State.h0000664000175000017500000001012515263665111021020 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/field/MultiFieldCreatorArray.cc0000664000175000017500000001216215263665111024456 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/field/FieldCreator.cc0000664000175000017500000000777615263665111022463 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/field/MissingValue.h0000664000175000017500000000556115263665111022356 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 ecmwf-atlas-b0f79ee/src/atlas/field/MultiFieldCreatorArray.h0000664000175000017500000000320315263665111024314 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/field/MultiFieldCreator.h0000664000175000017500000000546315263665111023327 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/field/MultiFieldCreatorIFS.h0000664000175000017500000000370115263665111023662 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/field/for_each.h0000664000175000017500000006363515263665111021524 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/field/MultiFieldCreator.cc0000664000175000017500000000276515263665111023467 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/field/FieldBuilder.cc0000664000175000017500000000177215263665111022440 0ustar alastairalastair/* * (C) Copyright 2026 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/FieldBuilder.h" #include "atlas/field/builders/GridBoxArea.h" namespace atlas { namespace field { namespace { void force_link() { static struct Link { Link() { FieldBuilderFactoryBuilder(); } } link; } } // namespace const AbstractFieldBuilder* FieldBuilderFactory::build(const util::Config& config, const util::ObjectHandleBase& object) { force_link(); const std::string type = config.getString("type"); return get(type)->make(config, object); } } // namespace field } // namespace atlasecmwf-atlas-b0f79ee/src/atlas/field/State.cc0000664000175000017500000001773115263665111021170 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/field/builders/0000775000175000017500000000000015263665111021401 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/field/builders/GridBoxArea.h0000664000175000017500000000557115263665111023711 0ustar alastairalastair/* * (C) Copyright 2026 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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/FieldBuilder.h" #include "atlas/functionspace/StructuredColumns.h" namespace atlas { namespace field { namespace builders { /** * \brief Build a field containing a representative grid-box area in square metres. * * The source object must be a FunctionSpace backed by * functionspace::StructuredColumns. The underlying projection must either be * `lonlat` or `rotated_lonlat`, with coordinates in degrees, or report its * coordinate units as metres. Other function spaces and projection units are * rejected. * * Grid boxes are bounded by the midpoints between neighbouring row and column * coordinates. The algorithm assumes that the longitudinal or projected * x-spacing is constant within each structured row. Consequently, one area is * computed per row and assigned to every local point in that row, including * halo points. Halo rows use StructuredColumns::compute_j() to select the * corresponding internal row spacing and StructuredColumns::compute_xy() to * obtain reflected boundary coordinates. * * For a spherical lon/lat grid, the area between longitudes * \f$\lambda_W,\lambda_E\f$ and latitudes \f$\phi_S,\phi_N\f$ is evaluated by * integrating the spherical surface element: * * \f[ * A = \int_{\lambda_W}^{\lambda_E}\int_{\phi_S}^{\phi_N} * R^2\cos(\phi)\,\mathrm{d}\phi\,\mathrm{d}\lambda * = R^2\left|\lambda_E-\lambda_W\right| * \left|\sin(\phi_N)-\sin(\phi_S)\right|. * \f] * * Angular quantities are converted from degrees to radians. A strip crossing * either pole is split at \f$\phi=\pm\frac{\pi}{2}\f$ so mirrored latitude * contributions do not cancel. * * For projections expressed in metres, each grid box is treated as a planar * rectangle: * * \f[ * A = \left|x_E-x_W\right|\,\left|y_N-y_S\right| * = \Delta x\,\left|y_N-y_S\right|. * \f] * * The result is a rank-one double-precision Field with no vertical levels. * Its name defaults to `area` and can be changed with `field_name`. The sphere * radius defaults to geometry::Earth().radius() and can be set with `radius`. * If `geometry` is supplied, its radius takes precedence over `radius`. */ class GridBoxArea : public AbstractFieldBuilder { public: GridBoxArea(const util::Config& config, const util::ObjectHandleBase& object); Field operator()() const override; private: functionspace::StructuredColumns fs_; std::string field_name_; double radius_; }; } // namespace builders } // namespace field } // namespace atlasecmwf-atlas-b0f79ee/src/atlas/field/builders/GridBoxArea.cc0000664000175000017500000001240415263665111024040 0ustar alastairalastair/* * (C) Copyright 2026 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/builders/GridBoxArea.h" #include #include #include "atlas/array.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/util/Geometry.h" namespace atlas { namespace field { namespace builders { namespace { class StructuredColumnsAreaComputer { public: StructuredColumnsAreaComputer(const functionspace::StructuredColumns& fs, double radius = geometry::Earth().radius()); double operator()(idx_t point_index) const; void operator()(double area[], std::size_t size) const; private: double sin_latitude_strip_width(double lat_a, double lat_b) const; private: const functionspace::StructuredColumns& fs_; double radius_; array::ArrayView index_i_; array::ArrayView index_j_; bool projection_in_degrees_; }; StructuredColumnsAreaComputer::StructuredColumnsAreaComputer(const functionspace::StructuredColumns& fs, double radius): fs_(fs), radius_(radius), index_i_(array::make_view(fs.index_i())), index_j_(array::make_view(fs.index_j())) { auto proj_type = fs_.projection().type(); if (proj_type == "rotated_lonlat" || proj_type == "lonlat") { projection_in_degrees_ = true; } else if (fs_.projection().units() == "meters") { projection_in_degrees_ = false; } else { ATLAS_DEBUG("Projection type = " << proj_type); throw_Exception("StructuredColumnsAreaComputer only works for (rotated) lonlat grids or projections in meters", Here()); } } double StructuredColumnsAreaComputer::operator()(idx_t point_index) const { idx_t i = index_i_(point_index); idx_t j = index_j_(point_index); PointXY xy = fs_.compute_xy(i, j); double y = xy.y(); double yN = 0.5 * (y + fs_.compute_xy(i, j - 1).y()); double yS = 0.5 * (y + fs_.compute_xy(i, j + 1).y()); idx_t j_internal = fs_.compute_j(j); const double dx = fs_.grid().dx(j_internal); if (projection_in_degrees_) { constexpr double deg2rad = M_PI / 180.0; return radius_ * radius_ * dx * deg2rad * sin_latitude_strip_width(yN, yS); } return dx * std::abs(yN - yS); } void StructuredColumnsAreaComputer::operator()(double area[], std::size_t size) const { ATLAS_ASSERT(size == fs_.size()); for (idx_t j = fs_.j_begin_halo(); j < fs_.j_end_halo(); ++j) { const idx_t i_begin = fs_.i_begin_halo(j); const idx_t i_end = fs_.i_end_halo(j); if (i_begin == i_end) { continue; } const idx_t first_row_point = fs_.index(i_begin, j); const double first_row_point_area = operator()(first_row_point); for (idx_t i = i_begin; i < i_end; ++i) { area[fs_.index(i, j)] = first_row_point_area; } } } double StructuredColumnsAreaComputer::sin_latitude_strip_width(double lat_a, double lat_b) const { constexpr double deg2rad = M_PI / 180.0; const double lat_max = std::max(lat_a, lat_b); const double lat_min = std::min(lat_a, lat_b); if (lat_max > 90. && lat_min < 90.) { return std::abs(std::sin(lat_max * deg2rad) - std::sin(90. * deg2rad)) + std::abs(std::sin(lat_min * deg2rad) - std::sin(90. * deg2rad)); } if (lat_min < -90. && lat_max > -90.) { return std::abs(std::sin(lat_max * deg2rad) - std::sin(-90. * deg2rad)) + std::abs(std::sin(lat_min * deg2rad) - std::sin(-90. * deg2rad)); } return std::abs(std::sin(lat_max * deg2rad) - std::sin(lat_min * deg2rad)); } } // namespace static FieldBuilderFactoryBuilder register_grid_box_area("grid-box-area"); GridBoxArea::GridBoxArea(const util::Config& config, const util::ObjectHandleBase& object) { const auto* fs = dynamic_cast(&object); if (not fs) { throw_Exception("GridBoxArea requires a FunctionSpace object handle", Here()); } fs_ = functionspace::StructuredColumns(*fs); if (not fs_) { throw_Exception("GridBoxArea only works for StructuredColumns function spaces", Here()); } field_name_ = config.getString("field_name", "area"); radius_ = geometry::Earth().radius(); config.get("radius", radius_); if (config.has("geometry")) { const std::string geometry_name = config.getString("geometry"); radius_ = Geometry(geometry_name).radius(); } } Field GridBoxArea::operator()() const { Field area = fs_.createField(option::name(field_name_) | option::levels(0)); StructuredColumnsAreaComputer area_computer(fs_, radius_); area_computer(array::make_view(area).data(), area.size()); return area; } } // namespace builders } // namespace field } // namespace atlasecmwf-atlas-b0f79ee/src/atlas/field/FieldCreatorIFS.cc0000664000175000017500000000500215263665111023001 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/field/FieldCreator.h0000664000175000017500000000541015263665111022304 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/field/Field.h0000664000175000017500000001576715263665111021004 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/runtime/0000775000175000017500000000000015263665111020170 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/runtime/Log.h0000664000175000017500000001230515263665111021063 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 namespace eckit { namespace mpi { class Comm; } // namespace mpi } // namespace eckit #include #include #include "atlas/library/detail/BlackMagic.h" #include "eckit/log/CodeLocation.h" namespace atlas { namespace detail { void debug_sync(const eckit::CodeLocation&, const eckit::mpi::Comm&); void debug_sync(const eckit::CodeLocation&); void debug_sync(const eckit::CodeLocation&, const eckit::mpi::Comm&, std::string_view); void debug_sync(const eckit::CodeLocation&, std::string_view); // Should not be needed because there's the std::string_view signature. // However it seems that due to missing `explicit` keyword in constructor // eckit::mpi::Comm(std::string_view) constructor, some compilers cannot disambiguate: // void debug_sync(const eckit::CodeLocation&, std::string_view); // void debug_sync(const eckit::CodeLocation&, const eckit::mpi::Comm&); // See https://github.com/ecmwf/atlas/issues/403#issuecomment-5777442503 void debug_sync(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_SYNC_HERE() \ do { \ ::atlas::detail::debug_sync(Here()); \ } while (0) #define ATLAS_DEBUG_SYNC_HERE_1(ARG1) \ do { \ ::atlas::detail::debug_sync(Here(), ARG1); \ } while (0) #define ATLAS_DEBUG_SYNC_HERE_2(ARG1, ARG2) \ do { \ ::atlas::detail::debug_sync(Here(), ARG1, ARG2); \ } while (0) #define ATLAS_DEBUG_SYNC(...) \ __ATLAS_SPLICE(__ATLAS_DEBUG_SYNC_, __ATLAS_NARG(__VA_ARGS__)) \ (__VA_ARGS__) #define __ATLAS_DEBUG_SYNC_0 ATLAS_DEBUG_SYNC_HERE #define __ATLAS_DEBUG_SYNC_1 ATLAS_DEBUG_SYNC_HERE_1 #define __ATLAS_DEBUG_SYNC_2 ATLAS_DEBUG_SYNC_HERE_2 #define __ATLAS_DEBUG_SYNC_VAR_1(VAR) \ do { \ std::ostringstream oss_debug_var; \ oss_debug_var << #VAR << " : " << VAR; \ ::atlas::detail::debug_sync(Here(), oss_debug_var.str()); \ } while (0) #define __ATLAS_DEBUG_SYNC_VAR_2(COMM, VAR) \ do { \ std::ostringstream oss_debug_var; \ oss_debug_var << #VAR << " : " << VAR; \ ::atlas::detail::debug_sync(Here(), COMM, oss_debug_var.str()); \ } while (0) #define ATLAS_DEBUG_SYNC_VAR(...) \ __ATLAS_SPLICE(__ATLAS_DEBUG_SYNC_VAR_, __ATLAS_NARG(__VA_ARGS__)) \ (__VA_ARGS__) ecmwf-atlas-b0f79ee/src/atlas/runtime/Exception.cc0000664000175000017500000000646715263665111022452 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/runtime/AtlasTool.h0000664000175000017500000000417315263665111022250 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/runtime/trace/0000775000175000017500000000000015263665111021266 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/runtime/trace/Logging.cc0000664000175000017500000000610615263665111023166 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/runtime/trace/CodeLocation.h0000664000175000017500000000402215263665111024000 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/runtime/trace/Nesting.cc0000664000175000017500000000121015263665111023176 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/runtime/trace/Barriers.h0000664000175000017500000000222115263665111023205 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/runtime/trace/CodeLocation.cc0000664000175000017500000000106515263665111024142 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/runtime/trace/CallStack.h0000664000175000017500000000303015263665111023274 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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_.reserve(64); } CallStack(const CallStack& other): stack_(other.begin(), other.end()), size_(other.size_) {} CallStack& operator=(const CallStack& other) { stack_.assign(other.begin(), other.end()); 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 ecmwf-atlas-b0f79ee/src/atlas/runtime/trace/Nesting.h0000664000175000017500000000256415263665111023055 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/runtime/trace/StopWatch.h0000664000175000017500000000332515263665111023356 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/runtime/trace/CallStack.cc0000664000175000017500000000336215263665111023442 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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) { auto hash = hash_codelocation(loc); if( ! id.empty() ) { hash = hash_combine( hash, std::hash{}(id) ); } if (stack_.size() == size_) { if (stack_.capacity() == size_) { stack_.reserve(std::max(2 * size_, 64)); } stack_.push_back(hash); } else { stack_[size_] = hash; } ++size_; hash_ = 0; } void CallStack::pop() { --size_; hash_ = 0; } 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 ecmwf-atlas-b0f79ee/src/atlas/runtime/trace/Barriers.cc0000664000175000017500000000472215263665111023353 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/runtime/trace/Timings.h0000664000175000017500000000237215263665111023055 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/runtime/trace/TraceT.h0000664000175000017500000001312515263665111022623 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/runtime/trace/Logging.h0000664000175000017500000000440315263665111023026 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/runtime/trace/Timings.cc0000664000175000017500000004214015263665111023210 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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() { // Leaking this singleton as it is used in static destructors and we don't want to rely on the order of destruction of static objects static TimingsRegistry* registry = new TimingsRegistry(); 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 ecmwf-atlas-b0f79ee/src/atlas/runtime/Trace.h0000664000175000017500000000532515263665111021404 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 //----------------------------------------------------------------------------------------------------------- ecmwf-atlas-b0f79ee/src/atlas/runtime/AtlasTool.cc0000664000175000017500000003000515263665111022377 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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' } ecmwf-atlas-b0f79ee/src/atlas/runtime/Log.cc0000664000175000017500000004450115263665111021224 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/config/Resource.h" #include #include #include #include "eckit/io/FDataSync.h" #include "eckit/config/Resource.h" #include "atlas/library/Library.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/trace/StopWatch.h" #include "atlas/runtime/Exception.h" // for MacOS backtrace #ifdef __APPLE__ #include #include #include #include #include #include #include #include #include #include #include namespace atlas { namespace { inline std::string hex_str(void* addr) { std::stringstream ss; ss << "0x" << std::hex << reinterpret_cast(addr); return ss.str(); }; inline std::vector atos_command_args(const Dl_info& info, const std::vector& runtime_addrs) { std::vector args{ "-fullPath", // Print the full path of the source files "-i", // Display inlined symbols "-o", info.dli_fname, // Path to the binary image containing the addresses "-l", hex_str(info.dli_fbase) // Load address of the binary image }; args.reserve(args.size() + runtime_addrs.size()); for (void* runtime_addr : runtime_addrs) { args.push_back(hex_str(runtime_addr)); } return args; } inline std::string full_command(const std::string& cmd, const std::vector& args) { std::stringstream ss; ss << cmd; for (const auto& arg : args) { ss << " " << arg; } return ss.str(); } inline std::string trim_ascii_whitespace(std::string_view text) { const auto begin = text.find_first_not_of(" \t\r\n"); if (begin == std::string_view::npos) { return {}; } const auto end = text.find_last_not_of(" \t\r\n"); return std::string(text.substr(begin, end - begin + 1)); } inline std::vector split_lines(std::string_view text) { std::vector lines; size_t begin = 0; while (begin < text.size()) { const auto end = text.find('\n', begin); const auto line = trim_ascii_whitespace(text.substr(begin, end == std::string_view::npos ? text.size() - begin : end - begin)); if (not line.empty()) { lines.push_back(line); } if (end == std::string_view::npos) { break; } begin = end + 1; } return lines; } inline std::string run_command(const std::string& cmd, const std::vector& args) { // This may not be all that safe in a multi-threaded context, or a signal handling context std::string full_cmd = full_command(cmd, args); std::string output; FILE* fp = ::popen(full_cmd.c_str(), "r"); if (fp) { char* line_buffer = nullptr; size_t line_len = 0; // getline dynamically allocates memory and handles ANY string size safely ssize_t chars_read = 0; while ((chars_read = getline(&line_buffer, &line_len, fp)) != -1) { const auto line = trim_ascii_whitespace( std::string_view(line_buffer, static_cast(chars_read))); if (line.empty()) { continue; } if (not output.empty()) { output.push_back('\n'); } output += line; } // POSIX requirement: Free the memory allocated by getline std::free(line_buffer); ::pclose(fp); } return output; } // This function is a custom backtrace implementation for MacOS that uses the atos command // (Apple's address-to-symbol utility) to resolve function names and source locations, // including line numbers, from the raw addresses obtained from backtrace. // It also filters out frames from Apple internal system libraries for better readability. std::string macos_backtrace() { auto simplify = [](std::string frame) { auto replace = [&frame] (const std::string& from, const std::string& to) { size_t start_pos = 0; while((start_pos = frame.find(from, start_pos)) != std::string::npos) { frame.replace(start_pos, from.length(), to); start_pos += to.length(); // Handles case where 'to' is a substring of 'from' } return frame; }; auto simplify_std_string = [&replace]() { replace("std::basic_string, std::allocator>", "std::string"); }; auto simplify_std_vector = [&frame]() { // Match std::vector with its type and remove the redundant allocator argument // Matches: std::vector> // Captures TYPE in group 1 std::regex allocatorRegex(R"(std::vector<([^,>]+),\s*std::allocator<\1\s*>>)"); // Replace the entire match with just std::vector frame = std::regex_replace(frame, allocatorRegex, "std::vector<$1>"); }; replace("std::__1::", "std::"); simplify_std_string(); simplify_std_vector(); return frame; }; void* callstack[128]; int frames = ::backtrace(callstack, 128); struct Frame { int frame_index; void* runtime_addr; Dl_info info; }; struct FrameBatch { Dl_info info; std::vector frames; }; struct FrameBatchKey { std::string image_path; void* image_base; bool operator==(const FrameBatchKey& other) const { return image_base == other.image_base && image_path == other.image_path; } FrameBatchKey(const Frame& frame) { image_path = frame.info.dli_fname ? frame.info.dli_fname : ""; image_base = frame.info.dli_fbase; } }; struct FrameBatchKeyHash { size_t operator()(const FrameBatchKey& key) const { return std::hash{}(key.image_path) ^ (std::hash{}(key.image_base) << 1); } }; std::vector pending_frames; pending_frames.reserve(frames); std::vector formatted_frames(frames); std::vector emit_frame(frames, false); for (int f = 0; f < frames; ++f) { Dl_info info; // Match the raw pointer address to its underlying loaded image binary if (dladdr(callstack[f], &info) && info.dli_fname) { // SKIP frame processing if it belongs to Apple internal system libraries if (std::string_view{info.dli_fname}.find("/usr/lib/") != std::string_view::npos || std::string_view{info.dli_fname}.find("/System/Library/") != std::string_view::npos) { continue; } pending_frames.push_back({f, callstack[f], info}); } else { emit_frame[f] = true; formatted_frames[f] = "Unknown Address: " + hex_str(callstack[f]); } } std::vector batches; batches.reserve(pending_frames.size()); std::unordered_map batch_index_by_image; batch_index_by_image.reserve(pending_frames.size()); for (const auto& pending_frame : pending_frames) { auto [it, inserted] = batch_index_by_image.emplace(pending_frame, batches.size()); if (inserted) { batches.push_back({pending_frame.info, {}}); } batches[it->second].frames.push_back(pending_frame); } std::vector resolved_frames(frames); std::vector frame_resolution_failed(frames, false); std::vector failed_commands(frames); for (const auto& batch : batches) { std::vector runtime_addrs; runtime_addrs.reserve(batch.frames.size()); for (const auto& pending_frame : batch.frames) { runtime_addrs.push_back(pending_frame.runtime_addr); } auto args = atos_command_args(batch.info, runtime_addrs); auto outputs = split_lines(run_command("atos", args)); if (outputs.size() != batch.frames.size()) { const auto command = full_command("atos", args); for (const auto& pending_frame : batch.frames) { frame_resolution_failed[pending_frame.frame_index] = true; failed_commands[pending_frame.frame_index] = command; } continue; } for (size_t idx = 0; idx < batch.frames.size(); ++idx) { resolved_frames[batch.frames[idx].frame_index] = outputs[idx]; } } for (const auto& pending_frame : pending_frames) { if (frame_resolution_failed[pending_frame.frame_index]) { emit_frame[pending_frame.frame_index] = true; formatted_frames[pending_frame.frame_index] = "[FRAME PROCESSING FAILED]: " + failed_commands[pending_frame.frame_index]; continue; } const auto& cmd_output = resolved_frames[pending_frame.frame_index]; if (cmd_output.empty()) { emit_frame[pending_frame.frame_index] = true; formatted_frames[pending_frame.frame_index] = "[FRAME PROCESSING FAILED]: " + full_command("atos", atos_command_args(pending_frame.info, std::vector{pending_frame.runtime_addr})); continue; } bool in_this_file = cmd_output.find("atlas/runtime/Log.cc") != std::string_view::npos; if (not in_this_file) { emit_frame[pending_frame.frame_index] = true; formatted_frames[pending_frame.frame_index] = simplify(cmd_output); } } int nb_emitted_frames = std::count(emit_frame.begin(), emit_frame.end(), true); std::stringstream out; for (int i = 0, f = 0; f < frames; ++f) { if (emit_frame[f]) { std::string frame_index_str = "#" + std::to_string(i++); out << std::left << std::setw(6) << frame_index_str << formatted_frames[f]; if (i < nb_emitted_frames) { out << '\n'; } } } return out.str(); } } } #endif #if ATLAS_HAVE_FORTRAN #include "fckit/Log.h" #endif namespace atlas { bool ATLAS_BACKTRACE_RESOLVE() { static bool resolve = eckit::Resource("$ATLAS_BACKTRACE_RESOLVE", true); return resolve; } std::string backtrace() { #if defined(__APPLE__) if (ATLAS_BACKTRACE_RESOLVE()) { return atlas::macos_backtrace(); } #endif return eckit::BackTrace::dump(); } namespace detail { namespace { double ATLAS_MPI_BARRIER_TIMEOUT() { static double timeout = eckit::Resource("$ATLAS_MPI_BARRIER_TIMEOUT", 3.); return timeout; } enum class BarrierTimeoutAction { ABORT, THROW, CONTINUE }; std::string_view to_string_view(BarrierTimeoutAction action) { switch (action) { case BarrierTimeoutAction::ABORT: return "ABORT"; case BarrierTimeoutAction::THROW: return "THROW"; case BarrierTimeoutAction::CONTINUE: return "CONTINUE"; } return "UNKNOWN"; } BarrierTimeoutAction to_BarrierTimeoutAction(const std::string& str) { if (str == "ABORT") { return BarrierTimeoutAction::ABORT; } if (str == "THROW") { return BarrierTimeoutAction::THROW; } if (str == "CONTINUE") { return BarrierTimeoutAction::CONTINUE; } ATLAS_THROW_EXCEPTION("Invalid value for ATLAS_MPI_BARRIER_TIMEOUT_ACTION: " << str << "." << "Valid options are: ABORT, THROW, CONTINUE."); } BarrierTimeoutAction ATLAS_MPI_BARRIER_TIMEOUT_ACTION() { static BarrierTimeoutAction action = to_BarrierTimeoutAction(eckit::Resource("$ATLAS_MPI_BARRIER_TIMEOUT_ACTION", "THROW")); return action; } bool mpi_barrier_timeout(const mpi::Comm& comm, double seconds) { if (seconds <= 0.) { return false; } auto req = comm.iBarrier(); const auto test_interval = std::chrono::milliseconds(10); const auto deadline = std::chrono::steady_clock::now() + std::chrono::duration(seconds); while (not req.test()) { std::this_thread::sleep_for(test_interval); if (std::chrono::steady_clock::now() > deadline) { return not req.test(); } } return false; } std::vector get_mpi_ranks_that_timed_out(const mpi::Comm& comm) { int local_timed_out = 0; const int size = comm.size(); const int rank = comm.rank(); const int tag = 0; std::vector all_timed_out(size); all_timed_out[rank] = local_timed_out; std::vector peers; peers.reserve(size > 0 ? size - 1 : 0); std::vector recv_requests; std::vector send_requests; recv_requests.reserve(size > 0 ? size - 1 : 0); send_requests.reserve(size > 0 ? size - 1 : 0); for (int peer = 0; peer < size; ++peer) { if (peer == rank) { continue; } peers.push_back(peer); recv_requests.push_back(comm.iReceive(all_timed_out[peer], peer, tag)); } for (int peer : peers) { send_requests.push_back(comm.iSend(local_timed_out, peer, tag)); } std::vector recv_completed(recv_requests.size(), false); std::vector send_completed(send_requests.size(), false); size_t recv_pending = recv_requests.size(); size_t send_pending = send_requests.size(); const auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(100); while (recv_pending > 0 || send_pending > 0) { for (size_t i = 0; i < recv_requests.size(); ++i) { if (not recv_completed[i] && recv_requests[i].test()) { recv_completed[i] = true; --recv_pending; } } for (size_t i = 0; i < send_requests.size(); ++i) { if (not send_completed[i] && send_requests[i].test()) { send_completed[i] = true; --send_pending; } } if (recv_pending == 0 && send_pending == 0) { break; } std::this_thread::sleep_for(std::chrono::milliseconds(100)); if (std::chrono::steady_clock::now() > deadline) { for (size_t i = 0; i < recv_requests.size(); ++i) { if (not recv_completed[i]) { all_timed_out[peers[i]] = 1; } } break; } } std::vector timed_out_ranks; for (int i = 0; i < all_timed_out.size(); ++i) { if (all_timed_out[i]) { timed_out_ranks.push_back(i); } } return timed_out_ranks; } void debug_mpi_barrier(const eckit::CodeLocation& here, const mpi::Comm& comm, std::string_view what = {}) { if (mpi_barrier_timeout(comm, ATLAS_MPI_BARRIER_TIMEOUT())) { std::ostringstream out; out << "DEBUG_SYNC[" << comm.name() << "]"; if (not what.empty()) { out << "(" << what << ")"; } out << " @ " << here << "\n"; out << "ATLAS_DEBUG_SYNC MPI barrier timed out on ranks " << get_mpi_ranks_that_timed_out(comm) << " in communicator '" << comm.name() << "' (${ATLAS_MPI_BARRIER_TIMEOUT}=" << ATLAS_MPI_BARRIER_TIMEOUT() << ", ${ATLAS_MPI_BARRIER_TIMEOUT_ACTION}=" << to_string_view(ATLAS_MPI_BARRIER_TIMEOUT_ACTION()) << ").\n" << "-----------------------------------------\n" << "BACKTRACE [rank=" << comm.rank() << "]\n" << "-----------------------------------------\n" << backtrace() << "\n" << "-----------------------------------------"; switch (ATLAS_MPI_BARRIER_TIMEOUT_ACTION()) { case BarrierTimeoutAction::ABORT: Log::error() << out.str() << " Calling MPI_Abort..." << std::endl; comm.abort(); break; case BarrierTimeoutAction::THROW: ATLAS_THROW_EXCEPTION("ATLAS_MPI_BARRIER_TIMEOUT: \n" << out.str()); break; case BarrierTimeoutAction::CONTINUE: Log::warning() << out.str() << " Continuing execution despite barrier timeout..." << std::endl; break; } } } void flush_and_sync_output_streams() { for( auto& channel : { &Log::info(), &Log::warning(), &Log::trace(), &Log::debug(), &Log::error()} ) { if (channel) { channel->flush(); } } std::cout.flush(); std::cerr.flush(); eckit::fdatasync(STDOUT_FILENO); eckit::fdatasync(STDERR_FILENO); } } // namespace void debug_sync(const eckit::CodeLocation& here, const mpi::Comm& comm) { flush_and_sync_output_streams(); debug_mpi_barrier(here, comm); Log::info() << "DEBUG_SYNC["< #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 ecmwf-atlas-b0f79ee/src/atlas/numerics/0000775000175000017500000000000015263665111020332 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/numerics/Method.cc0000664000175000017500000000205715263665111022065 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/numerics/fvm/0000775000175000017500000000000015263665111021122 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/numerics/fvm/Method.cc0000664000175000017500000001236615263665111022661 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/numerics/fvm/Nabla.cc0000664000175000017500000006011515263665111022451 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/numerics/fvm/Method.h0000664000175000017500000000466115263665111022522 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/numerics/fvm/Nabla.h0000664000175000017500000000323415263665111022312 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/numerics/Nabla.cc0000664000175000017500000001466215263665111021667 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/numerics/Method.h0000664000175000017500000000165015263665111021725 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/numerics/Nabla.h0000664000175000017500000000732715263665111021531 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/trans/0000775000175000017500000000000015263665111017634 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/trans/detail/0000775000175000017500000000000015263665111021076 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/trans/detail/TransFactory.h0000664000175000017500000001130615263665111023667 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/trans/detail/TransInterface.cc0000664000175000017500000002472115263665111024323 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/trans/detail/TransFactory.cc0000664000175000017500000001776115263665111024040 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/trans/detail/TransInterface.h0000664000175000017500000001430615263665111024163 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/trans/detail/TransImpl.h0000664000175000017500000001712215263665111023163 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/trans/detail/TransImpl.cc0000664000175000017500000000117015263665111023315 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/trans/VorDivToUV.h0000664000175000017500000001032715263665111021777 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/trans/Trans.h0000664000175000017500000001717615263665111021110 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/trans/LegendreCacheCreator.h0000664000175000017500000000620415263665111024000 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/trans/VorDivToUV.cc0000664000175000017500000001232715263665111022137 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/trans/ifs/0000775000175000017500000000000015263665111020415 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/trans/ifs/LegendreCacheCreatorIFS.h0000664000175000017500000000264415263665111025127 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/trans/ifs/VorDivToUVIFS.cc0000664000175000017500000000454115263665111023261 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/trans/ifs/TransIFSStructuredColumns.cc0000664000175000017500000000350515263665111026006 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/trans/ifs/TransIFSStructuredColumns.h0000664000175000017500000000273515263665111025654 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/trans/ifs/TransIFSNodeColumns.cc0000664000175000017500000000304415263665111024525 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/trans/ifs/VorDivToUVIFS.h0000664000175000017500000000400015263665111023111 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/trans/ifs/TransIFS.h0000664000175000017500000005030615263665111022223 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/trans/ifs/TransIFS.cc0000664000175000017500000033443415263665111022370 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/JSON.h" #include "atlas/library/config.h" #if ATLAS_HAVE_ECTRANS #include "ectrans/transi.h" #else #include "transi/trans.h" #endif #include "pluto/pluto.h" #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"); 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(); } template class PooledBuffer { public: explicit PooledBuffer(const std::size_t size): size_(size), allocator_(pluto::host_pool_resource()), data_(nullptr) { if (size_) { data_ = allocator_.allocate(size_); #if ATLAS_INIT_SNAN for (std::size_t i = 0; i < size_; ++i) { data_[i] = invalid_value(); } #endif } } ~PooledBuffer() { if (data_) { allocator_.deallocate(data_, size_); } } PooledBuffer(const PooledBuffer&) = delete; PooledBuffer& operator=(const PooledBuffer&) = delete; Value* data() { return data_; } Value& operator[](const std::size_t index) { return data_[index]; } private: std::size_t size_; pluto::allocator allocator_; Value* data_{nullptr}; }; } // 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(": fieldset 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 { 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 { 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())); 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())); 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); PooledBuffer 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 PooledBuffer rgp(nfld * ngptot()); PooledBuffer 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 PooledBuffer rgp(nfld * ngptot()); PooledBuffer 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 PooledBuffer rgp(nfld * ngptot()); PooledBuffer 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() != spvor.shape()) { std::string vorDims; for (int item : spvor.shape()) { vorDims += std::to_string(item) + " "; } std::string divDims; for (int item : spdiv.shape()) { divDims += std::to_string(item) + " "; } std::string msg = "dirtrans_wind2vordiv: vorticity not compatible with divergence. Vorticity dimensions: " + vorDims + ", Divergence dimensions: " + divDims; throw_Exception(msg, Here()); } const size_t nwindfld = compute_nfld(gpwind); if (nwindfld != 2 * nfld && nwindfld != 3 * nfld) { throw_Exception("dirtrans_wind2vordiv:wind field is not compatible with vorticity, divergence.", Here()); } if (spdiv.shape(0) != nspec2()) { std::stringstream msg; msg << "dirtrans_wind2vordiv: 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_wind2vordiv: spectral vorticity field is empty.", Here()); } if (spdiv.size() == 0) { throw_Exception("dirtrans_wind2vordiv: spectral divergence field is empty.", Here()); } // Arrays Trans expects PooledBuffer rgp(2 * nfld * ngptot()); PooledBuffer rspvor(nspec2() * nfld); PooledBuffer 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() != spvor.shape()) { std::string vorDims; for (int item : spvor.shape()) { vorDims += std::to_string(item) + " "; } std::string divDims; for (int item : spdiv.shape()) { divDims += std::to_string(item) + " "; } std::string msg = "dirtrans_wind2vordiv: vorticity not compatible with divergence. Vorticity dimensions: " + vorDims + ", Divergence dimensions: " + divDims; throw_Exception(msg, Here()); } const size_t nwindfld = compute_nfld(gpwind); if (nwindfld != 2 * nfld && nwindfld != 3 * nfld) { throw_Exception("dirtrans_wind2vordiv: wind field is not compatible with vorticity, divergence.", Here()); } if (spdiv.shape(0) != nspec2()) { std::stringstream msg; msg << "dirtrans_wind2vordiv: 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_wind2vordiv: spectral vorticity field is empty.", Here()); } if (spdiv.size() == 0) { throw_Exception("dirtrans_wind2vordiv: spectral divergence field is empty.", Here()); } // Arrays Trans expects PooledBuffer rgp(2 * nfld * ngptot()); PooledBuffer rspvor(nspec2() * nfld); PooledBuffer 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 PooledBuffer rgp(nfld * ngptot()); PooledBuffer rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); rspview.assign(0.); // 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_adj: different number of gridpoint fields than spectral fields", Here()); } if ((int)gpfield.shape(0) < ngptot()) { throw_Exception("dirtrans_adj: slowest moving index must be >= ngptot", Here()); } const int nfld = compute_nfld(gpfield); // Arrays Trans expects PooledBuffer rgp(nfld * ngptot()); PooledBuffer rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); rspview.assign(0.); // 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_adj: different number of gridpoint fields than spectral fields", Here()); } if ((int)gpfield.shape(0) < ngptot()) { throw_Exception("dirtrans_adj: slowest moving index must be >= ngptot", Here()); } const int nfld = compute_nfld(gpfield); // Arrays Trans expects PooledBuffer rgp(nfld * ngptot()); PooledBuffer rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); rspview.assign(0.); // 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 PooledBuffer rgp(nfld * ngptot()); PooledBuffer rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); rspview.assign(0.); // 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() != spvor.shape()) { std::string vorDims; for (int item : spvor.shape()) { vorDims += std::to_string(item) + " "; } std::string divDims; for (int item : spdiv.shape()) { divDims += std::to_string(item) + " "; } std::string msg = "dirtrans_wind2vordiv_adj: vorticity not compatible with divergence. Vorticity dimensions: " + vorDims + ", Divergence dimensions: " + divDims; throw_Exception(msg, Here()); } const size_t nwindfld = compute_nfld(gpwind); if (nwindfld != 2 * nfld && nwindfld != 3 * nfld) { throw_Exception("dirtrans_wind2vordiv_adj:wind field is not compatible with vorticity, divergence.", Here()); } if (spdiv.shape(0) != nspec2()) { std::stringstream msg; msg << "dirtrans_wind2vordiv_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_wind2vordiv_adj: spectral vorticity field is empty.", Here()); } if (spdiv.size() == 0) { throw_Exception("dirtrans_wind2vordiv_adj: spectral divergence field is empty.", Here()); } // Arrays Trans expects PooledBuffer rgp(2 * nfld * ngptot()); PooledBuffer rspvor(nspec2() * nfld); PooledBuffer 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)); rspvorview.assign(0.); rspdivview.assign(0.); // 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() != spvor.shape()) { std::string vorDims; for (int item : spvor.shape()) { vorDims += std::to_string(item) + " "; } std::string divDims; for (int item : spdiv.shape()) { divDims += std::to_string(item) + " "; } std::string msg = "dirtrans_wind2vordiv_adj: vorticity not compatible with divergence. Vorticity dimensions: " + vorDims + ", Divergence dimensions: " + divDims; throw_Exception(msg, Here()); } const size_t nwindfld = compute_nfld(gpwind); if (nwindfld != 2 * nfld && nwindfld != 3 * nfld) { throw_Exception("dirtrans_wind2vordiv_adj:wind field is not compatible with vorticity, divergence.", Here()); } if (spdiv.shape(0) != nspec2()) { std::stringstream msg; msg << "dirtrans_wind2vordiv_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_wind2vordiv_adj: spectral vorticity field is empty.", Here()); } if (spdiv.size() == 0) { throw_Exception("dirtrans_wind2vordiv_adj: spectral divergence field is empty.", Here()); } // Arrays Trans expects PooledBuffer rgp(2 * nfld * ngptot()); PooledBuffer rspvor(nspec2() * nfld); PooledBuffer 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)); rspvorview.assign(0.); rspdivview.assign(0.); // 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 PooledBuffer rgp(nfld * ngptot()); PooledBuffer 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 PooledBuffer rgp(nfld * ngptot()); PooledBuffer 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 PooledBuffer rgp(nfld * ngptot()); PooledBuffer 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 PooledBuffer rgp(3 * nfld * ngptot()); // (scalars) + (NS ders) + (EW ders) PooledBuffer 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); } ++f; } } 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 PooledBuffer rgp(3 * nfld * ngptot()); // (scalars) + (NS ders) + (EW ders) PooledBuffer 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()); ++f; } } 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() != spvor.shape()) { std::string vorDims; for (int item : spvor.shape()) { vorDims += std::to_string(item) + " "; } std::string divDims; for (int item : spdiv.shape()) { divDims += std::to_string(item) + " "; } std::string msg = "invtrans_vordiv2wind: vorticity not compatible with divergence. Vorticity dimensions: " + vorDims + ", Divergence dimensions: " + divDims; throw_Exception(msg, Here()); } ATLAS_ASSERT(spvor.rank() == 1 || spvor.rank() == 2, "Only rank-1 and rank-2 spectral fields are supported"); const int nwindfld = compute_nfld(gpwind); if (nwindfld != 2 * nfld && nwindfld != 3 * nfld) { throw_Exception("invtrans_vordiv2wind: wind field is not compatible with vorticity, divergence.", Here()); } if (spdiv.shape(0) != nspec2()) { std::stringstream msg; msg << "invtrans_vordiv2wind: 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: spectral vorticity field is empty.", Here()); } if (spdiv.size() == 0) { throw_Exception("invtrans_vordiv2wind: spectral divergence field is empty.", Here()); } // Arrays Trans expects PooledBuffer rgp(2 * nfld * ngptot()); PooledBuffer rspvor(nspec2() * nfld); PooledBuffer 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() != spvor.shape()) { std::string vorDims; for (int item : spvor.shape()) { vorDims += std::to_string(item) + " "; } std::string divDims; for (int item : spdiv.shape()) { divDims += std::to_string(item) + " "; } std::string msg = "invtrans_vordiv2wind: vorticity not compatible with divergence. Vorticity dimensions: " + vorDims + ", Divergence dimensions: " + divDims; throw_Exception(msg, Here()); } ATLAS_ASSERT(spvor.rank() == 1 || spvor.rank() == 2, "Only rank-1 and rank-2 spectral fields are supported"); const int nwindfld = compute_nfld(gpwind); if (nwindfld != 2 * nfld && nwindfld != 3 * nfld) { throw_Exception("invtrans_vordiv2wind: wind field is not compatible with vorticity, divergence.", Here()); } if (spdiv.shape(0) != nspec2()) { std::stringstream msg; msg << "invtrans_vordiv2wind: 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: spectral vorticity field is empty.", Here()); } if (spdiv.size() == 0) { throw_Exception("invtrans_vordiv2wind: spectral divergence field is empty.", Here()); } // Arrays Trans expects PooledBuffer rgp(2 * nfld * ngptot()); PooledBuffer rspvor(nspec2() * nfld); PooledBuffer 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("invtrans_adj: different number of gridpoint fields than spectral fields", Here()); } if ((int)gpfield.shape(0) < ngptot()) { throw_Exception("invtrans_adj: slowest moving index must be >= ngptot", Here()); } const int nfld = compute_nfld(gpfield); // Arrays Trans expects PooledBuffer rgp(nfld * ngptot()); PooledBuffer rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); rspview.assign(0.); // 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 PooledBuffer rgp(nfld * ngptot()); PooledBuffer rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); rspview.assign(0.); // 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 PooledBuffer rgp(nfld * ngptot()); PooledBuffer rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); rspview.assign(0.); // 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 int nb_gridpoint_field = 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 nfld = compute_nfld(spfields); if (nb_gridpoint_field != 2 * nfld) { // factor 2 because N-S and E-W derivatives throw_Exception("invtrans_grad_adj: different number of gridpoint fields than spectral fields", Here()); } // Arrays Trans expects PooledBuffer rgp(3 * nfld * ngptot()); // (scalars) + (NS ders) + (EW ders) PooledBuffer rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(3 * nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); rspview.assign(0.); rgpview.assign(0.); // Pack gradients: mirror of the forward unpack { int f = nfld; 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) { rgpview(f, jnode) = field(jnode, jlev, 1 - dim); } ++f; } } else { auto field = make_view(gradfields[jfld]); for (idx_t jnode = 0; jnode < nb_nodes; ++jnode) { rgpview(f, jnode) = field(jnode, 1 - dim); } ++f; } } } } // 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 int nb_gridpoint_field = compute_nfld(gradfields); for (idx_t jfld = 0; jfld < gradfields.size(); ++jfld) { const Field& f = gradfields[jfld]; ATLAS_ASSERT(f.functionspace() == 0 || functionspace::NodeColumns(f.functionspace())); } 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_adj: different number of gridpoint fields than spectral fields", Here()); } // Arrays Trans expects PooledBuffer rgp(3 * nfld * ngptot()); // (scalars) + (NS ders) + (EW ders) PooledBuffer rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(3 * nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); rspview.assign(0.); rgpview.assign(0.); // Pack gradients: mirror of the forward unpack { mesh::IsGhostNode is_ghost(gp.nodes()); int f = nfld; 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)) { rgpview(f, n) = field(jnode, jlev, 1 - dim); ++n; } } ATLAS_ASSERT(n == ngptot()); ++f; } } else { auto field = make_view(gradfields[jfld]); int n = 0; for (idx_t jnode = 0; jnode < nb_nodes; ++jnode) { if (!is_ghost(jnode)) { rgpview(f, n) = field(jnode, 1 - dim); ++n; } } ATLAS_ASSERT(n == ngptot()); ++f; } } } } // 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() != spvor.shape()) { std::string vorDims; for (int item : spvor.shape()) { vorDims += std::to_string(item) + " "; } std::string divDims; for (int item : spdiv.shape()) { divDims += std::to_string(item) + " "; } std::string msg = "invtrans_vordiv2wind_adj: vorticity not compatible with divergence. Vorticity dimensions: " + vorDims + ", Divergence dimensions: " + divDims; throw_Exception(msg, Here()); } const size_t nwindfld = compute_nfld(gpwind); if (nwindfld != 2 * nfld && nwindfld != 3 * nfld) { throw_Exception("invtrans_vordiv2wind_adj: 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.", Here()); } if (spdiv.size() == 0) { throw_Exception("invtrans_vordiv2wind_adj: spectral divergence field is empty.", Here()); } // Arrays Trans expects PooledBuffer rgp(2 * nfld * ngptot()); PooledBuffer rspvor(nspec2() * nfld); PooledBuffer 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)); rspvorview.assign(0.); rspdivview.assign(0.); // 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() != spvor.shape()) { std::string vorDims; for (int item : spvor.shape()) { vorDims += std::to_string(item) + " "; } std::string divDims; for (int item : spdiv.shape()) { divDims += std::to_string(item) + " "; } std::string msg = "invtrans_vordiv2wind_adj: vorticity not compatible with divergence. Vorticity dimensions: " + vorDims + ", Divergence dimensions: " + divDims; throw_Exception(msg, Here()); } const size_t nwindfld = compute_nfld(gpwind); if (nwindfld != 2 * nfld && nwindfld != 3 * nfld) { throw_Exception("invtrans_vordiv2wind_adj: 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.", Here()); } if (spdiv.size() == 0) { throw_Exception("invtrans_vordiv2wind_adj: spectral divergence field is empty.", Here()); } // Arrays Trans expects PooledBuffer rgp(2 * nfld * ngptot()); PooledBuffer rspvor(nspec2() * nfld); PooledBuffer 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)); rspvorview.assign(0.); rspdivview.assign(0.); // 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 ecmwf-atlas-b0f79ee/src/atlas/trans/ifs/TransIFSNodeColumns.h0000664000175000017500000000264715263665111024377 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/trans/ifs/LegendreCacheCreatorIFS.cc0000664000175000017500000000766215263665111025272 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/trans/Cache.cc0000664000175000017500000000701215263665111021146 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/trans/Trans.cc0000664000175000017500000002313415263665111021235 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/trans/LegendreCacheCreator.cc0000664000175000017500000001111715263665111024135 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/trans/local/0000775000175000017500000000000015263665111020726 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/trans/local/VorDivToUVLocal.cc0000664000175000017500000002105515263665111024202 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/trans/local/LegendreCacheCreatorLocal.h0000664000175000017500000000265315263665111026051 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/trans/local/VorDivToUVLocal.h0000664000175000017500000000401015263665111024034 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/trans/local/TransLocal.h0000664000175000017500000002711115263665111023143 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/trans/local/LegendreCacheCreatorLocal.cc0000664000175000017500000001136115263665111026203 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/trans/local/LegendrePolynomials.cc0000664000175000017500000002523315263665111025216 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/trans/local/LegendrePolynomials.h0000664000175000017500000000550215263665111025055 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/trans/local/TransLocal.cc0000664000175000017500000022731715263665111023313 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 "pluto/pluto.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"); template class PooledBuffer { public: explicit PooledBuffer(const std::size_t size): size_(size), allocator_(pluto::host_pool_resource()), data_(nullptr) { if (size_) { data_ = allocator_.allocate(size_); } } ~PooledBuffer() { if (data_) { allocator_.deallocate(data_, size_); } } PooledBuffer(const PooledBuffer&) = delete; PooledBuffer& operator=(const PooledBuffer&) = delete; Value* data() { return data_; } Value& operator[](const std::size_t index) { return data_[index]; } private: std::size_t size_; pluto::allocator allocator_; Value* data_{nullptr}; }; } // 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; PooledBuffer lats(nlatsLeg_); PooledBuffer 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; } PooledBuffer 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() == spdiv.rank(), "Vorticity and divergence ranks must match"); ATLAS_ASSERT(spvor.shape() == spdiv.shape(), "Vorticity and divergence shapes must match"); ATLAS_ASSERT(spvor.rank() == 1 || spvor.rank() == 2, "Only rank-1 and rank-2 spectral fields are supported"); if (spvor.rank() == 2) { // Rank-2 spectral fields require a rank-3 wind field // The wind has dimensions (grid points, levels, 2) -- (2 for u and v) // The spectral fields have dimensions (levels, spectral coefficients) // The function invtrans expects: // - vorticity_spectra and divergence_spectra to be of size (levels * spectral coefficients) // - wind to be of layout (2, levels, grid points) // Therefore, we need to transpose the wind data after the inverse transform const int nb_vordiv_fields = static_cast(spvor.shape(1)); ATLAS_ASSERT(gpwind.rank() == 3, "Rank-2 spectral fields require a rank-3 wind field"); ATLAS_ASSERT(gpwind.shape(0) == grid().size(), "Wind horizontal dimension must match the grid size"); ATLAS_ASSERT(gpwind.shape(1) == nb_vordiv_fields, "Wind levels must match the spectral fields"); ATLAS_ASSERT(gpwind.shape(2) == 2, "Wind field must have two components"); const auto vorticity_spectra = array::make_view(spvor); const auto divergence_spectra = array::make_view(spdiv); auto gp_fields = array::make_view(gpwind); const 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); // The wind data is transposed compared to what invtrans is expecting, // so we need to allocate a temporary array to hold the transformed wind data const idx_t nb_grid_points = grid().size(); const idx_t transformed_wind_stride_component = nb_vordiv_fields * nb_grid_points; const idx_t transformed_wind_stride_level = nb_grid_points; PooledBuffer transformed_wind_data(2 * nb_vordiv_fields * nb_grid_points); auto transformed_wind = [&transformed_wind_stride_component, &transformed_wind_stride_level, &transformed_wind_data](idx_t component, idx_t level, idx_t point) { return transformed_wind_data[component * transformed_wind_stride_component + level * transformed_wind_stride_level + point]; }; // Perform the inverse transform to get the wind data in the temporary array invtrans(nb_vordiv_fields, vorticity_spectra.data(), divergence_spectra.data(), transformed_wind_data.data(), config); // Transpose the temporary transformed wind data into the gp_fields array for (idx_t point = 0; point < nb_grid_points; ++point) { for (idx_t level = 0; level < nb_vordiv_fields; ++level) { gp_fields(point, level, 0) = transformed_wind(0, level, point); gp_fields(point, level, 1) = transformed_wind(1, level, point); } } return; } 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"); PooledBuffer 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; int nb_scalar_ext = 2 * legendre_size(truncation_ + 1) * nb_scalar_fields; PooledBuffer U_ext(nb_vordiv_spec_ext); PooledBuffer V_ext(nb_vordiv_spec_ext); PooledBuffer scalar_ext(nb_scalar_ext); PooledBuffer vorticity_spectra_extended(nb_vordiv_spec_ext); PooledBuffer divergence_spectra_extended(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) { 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; PooledBuffer 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 ecmwf-atlas-b0f79ee/src/atlas/trans/Cache.h0000664000175000017500000001014115263665111021005 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/array.h0000664000175000017500000000207015263665111017773 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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" ecmwf-atlas-b0f79ee/src/atlas/array_fwd.h0000664000175000017500000000713515263665111020642 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/meshgenerator.h0000664000175000017500000000074315263665111021525 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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" ecmwf-atlas-b0f79ee/src/atlas/library/0000775000175000017500000000000015263665111020151 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/library/defines.h.in0000664000175000017500000001354015263665111022347 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 ecmwf-atlas-b0f79ee/src/atlas/library/detail/0000775000175000017500000000000015263665111021413 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/library/detail/BlackMagic.h0000664000175000017500000001211715263665111023543 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the 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) ecmwf-atlas-b0f79ee/src/atlas/library/detail/Debug.h0000664000175000017500000000773715263665111022630 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/library/FloatingPointExceptions.h0000664000175000017500000000571315263665111025147 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/library/Library.cc0000664000175000017500000004611615263665111022074 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/library/FloatingPointExceptions.cc0000664000175000017500000004003115263665111025275 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/library/Library.h0000664000175000017500000000650615263665111021735 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/library/Plugin.h0000664000175000017500000000174615263665111021570 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/library/config.h0000664000175000017500000000346115263665111021573 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/library/version.h.in0000664000175000017500000000250115263665111022412 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the 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_VERSION_STR "@atlas_VERSION_STR@" #define ATLAS_VERSION "@atlas_VERSION@" #define ATLAS_VERSION_MAJOR @atlas_VERSION_MAJOR@ #define ATLAS_VERSION_MINOR @atlas_VERSION_MINOR@ #define ATLAS_VERSION_PATCH @atlas_VERSION_PATCH@ #define ATLAS_VERSION_AT_LEAST(major, minor, patch) \ ((ATLAS_VERSION_MAJOR > (major)) || \ (ATLAS_VERSION_MAJOR == (major) && ATLAS_VERSION_MINOR > (minor)) || \ (ATLAS_VERSION_MAJOR == (major) && ATLAS_VERSION_MINOR == (minor) && ATLAS_VERSION_PATCH >= (patch))) 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; } } } ecmwf-atlas-b0f79ee/src/atlas/library/git_sha1.h.in0000664000175000017500000000145015263665111022426 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the 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 ecmwf-atlas-b0f79ee/src/atlas/library/Plugin.cc0000664000175000017500000000172215263665111021720 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/library.h0000664000175000017500000000072715263665111020330 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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" ecmwf-atlas-b0f79ee/src/atlas/mdspan.h0000664000175000017500000000776015263665111020152 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 // ------------------------------------------------------------------------------------------------ ecmwf-atlas-b0f79ee/src/atlas/util/0000775000175000017500000000000015263665111017462 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/util/Unique.h0000664000175000017500000002155415263665111021110 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/util/ObjectHandle.h0000664000175000017500000000466415263665111022167 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/util/Unique.cc0000664000175000017500000000304715263665111021243 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/util/KDTree.cc0000664000175000017500000001262115263665111021111 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/util/PeriodicTransform.h0000664000175000017500000000370215263665111023267 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/util/detail/0000775000175000017500000000000015263665111020724 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/util/detail/KDTree.h0000664000175000017500000003765415263665111022232 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/util/detail/filesystem.h0000664000175000017500000000034615263665111023264 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); } ecmwf-atlas-b0f79ee/src/atlas/util/detail/filesystem.cc0000664000175000017500000000467715263665111023435 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()); } } ecmwf-atlas-b0f79ee/src/atlas/util/detail/Cache.h0000664000175000017500000000654415263665111022111 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/util/Allocate.h0000664000175000017500000000506415263665111021364 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/util/SphericalPolygon.cc0000664000175000017500000000460215263665111023255 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/util/Factory.cc0000664000175000017500000001263715263665111021411 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/util/PolygonXY.cc0000664000175000017500000001320715263665111021704 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/util/Rotation.h0000664000175000017500000000325615263665111021440 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/util/CGALSphericalTriangulation.h0000664000175000017500000000362115263665111024737 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; } } } ecmwf-atlas-b0f79ee/src/atlas/util/NormaliseLongitude.h0000664000175000017500000000251215263665111023437 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the 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 ecmwf-atlas-b0f79ee/src/atlas/util/KDTree.h0000664000175000017500000002413115263665111020752 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/util/ObjectHandle.cc0000664000175000017500000000307715263665111022322 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/ObjectHandle.h" #include #include "atlas/runtime/Log.h" #include "atlas/util/Object.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 ecmwf-atlas-b0f79ee/src/atlas/util/QhullSphericalTriangulation.h0000664000175000017500000000363515263665111025323 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; } } } ecmwf-atlas-b0f79ee/src/atlas/util/Earth.h0000664000175000017500000000241515263665111020700 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/util/DataType.cc0000664000175000017500000000214615263665111021507 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/util/GridPointsJSONWriter.h0000664000175000017500000000233615263665111023610 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 atlasecmwf-atlas-b0f79ee/src/atlas/util/PackVectorFields.h0000664000175000017500000000257115263665111023030 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 ecmwf-atlas-b0f79ee/src/atlas/util/Polygon.h0000664000175000017500000001576315263665111021276 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/util/Rotation.cc0000664000175000017500000001454715263665111021603 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/util/CGALSphericalTriangulation.cc0000664000175000017500000001324115263665111025074 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 ecmwf-atlas-b0f79ee/src/atlas/util/ConvexSphericalPolygon.cc0000664000175000017500000004645515263665111024454 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/util/DataType.h0000664000175000017500000003134715263665111021356 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/util/Object.cc0000664000175000017500000000064715263665111021206 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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" ecmwf-atlas-b0f79ee/src/atlas/util/Allocate.cc0000664000175000017500000000572515263665111021526 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/util/Metadata.h0000664000175000017500000001026515263665111021357 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 "eckit/log/JSON.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&, idx_t root, const std::string& mpi_comm); void broadcast(Metadata&) const; void broadcast(Metadata&, idx_t root) const; void broadcast(Metadata&, idx_t root, const std::string& mpi_comm) const; size_t footprint() const; std::string json(eckit::JSON::Formatting = eckit::JSON::Formatting::indent()) 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 ecmwf-atlas-b0f79ee/src/atlas/util/ConvexSphericalPolygon.h0000664000175000017500000001407715263665111024311 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/util/function/0000775000175000017500000000000015263665111021307 5ustar alastairalastairecmwf-atlas-b0f79ee/src/atlas/util/function/SolidBodyRotation.h0000664000175000017500000000314715263665111025075 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 ecmwf-atlas-b0f79ee/src/atlas/util/function/MDPI_functions.h0000664000175000017500000000262715263665111024310 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the 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 ecmwf-atlas-b0f79ee/src/atlas/util/function/SolidBodyRotation.cc0000664000175000017500000001171115263665111025227 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 #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 ecmwf-atlas-b0f79ee/src/atlas/util/function/VortexRollup.cc0000664000175000017500000000265015263665111024306 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/util/function/MDPI_functions.cc0000664000175000017500000001100615263665111024435 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/util/function/SphericalHarmonic.cc0000664000175000017500000001201315263665111025206 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/function/SphericalHarmonic.h" #include #include #include #include #include #include #include "atlas/runtime/Exception.h" #include "atlas/util/Constants.h" namespace atlas::util::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))); } template static double cached(const Key& key, const double argument, Compute&& compute) { struct LastValue { bool initialized{}; Key key{}; double argument{}; double value{}; }; static thread_local LastValue last; if (last.initialized && last.key == key && last.argument == argument) { return last.value; } const double value = compute(); last = {true, key, argument, value}; return last.value; } static double P_cos_COLAT(const int n, const int abs_m, const double lat) { return cached(std::make_pair(n, abs_m), lat, [&] { const double colat_rad = (90. - lat) * Constants::degreesToRadians(); return P(n, abs_m, std::cos(colat_rad)); }); } } // namespace function struct SphericalHarmonic::Cache { explicit Cache(const std::uint64_t id): id(id) {} std::uint64_t id; std::unordered_map values; std::mutex mutex; }; double SphericalHarmonic::P_cos_COLAT(const double lat) const { struct LastValue { std::uint64_t cache_id{}; double lat{}; double value{}; }; static thread_local LastValue last; if (last.cache_id == cache_->id && last.lat == lat) { return last.value; } std::lock_guard lock(cache_->mutex); const auto cached_value = cache_->values.find(lat); if (cached_value != cache_->values.end()) { last = {cache_->id, lat, cached_value->second}; return last.value; } const double colat_rad = (90. - lat) * Constants::degreesToRadians(); const double value = P(n_, abs_m_, std::cos(colat_rad)); cache_->values.emplace(lat, value); last = {cache_->id, lat, value}; return last.value; } double spherical_harmonic(int n, int m, double lon, double lat) { const int abs_m = std::abs(m); ATLAS_ASSERT(n >= abs_m); if (m == 0) { return K(n, 0) * P_cos_COLAT(n, 0, lat); } auto K_SQRT2 = [](int n, int abs_m) { struct LastValue { int n{-1}; // Always initialize to invalid value to avoid false cache hits int abs_m{}; double value{}; }; static thread_local LastValue last; if (not (last.n == n && last.abs_m == abs_m)) { last = {n, abs_m, K(n, abs_m) * M_SQRT2}; } return last.value; }; lon *= Constants::degreesToRadians(); if (m > 0) { return (K_SQRT2(n, abs_m) * std::cos(abs_m * lon) * P_cos_COLAT(n, abs_m, lat)); } else { // m < 0 // When m is less than 0, multiply - 1 in advance and send it to K return (K_SQRT2(n, abs_m) * std::sin(abs_m * lon) * P_cos_COLAT(n, abs_m, lat)); } } SphericalHarmonic::SphericalHarmonic(const int n, const int m): n_(n), m_(m), abs_m_(std::abs(m)) { ATLAS_ASSERT(n_ >= abs_m_); static std::atomic next_cache_id{1}; cache_ = std::make_shared(next_cache_id.fetch_add(1, std::memory_order_relaxed)); Knm_ = K(n_, abs_m_) * (m_ == 0 ? 1. : M_SQRT2); } double SphericalHarmonic::operator()(double lon, double lat) const { if (m_ == 0) { return Knm_ * P_cos_COLAT(lat); } lon *= Constants::degreesToRadians(); if (m_ > 0) { return Knm_ * std::cos(m_ * lon) * P_cos_COLAT(lat); } return Knm_ * std::sin(-m_ * lon) * P_cos_COLAT(lat); } } // namespace atlas::util::function ecmwf-atlas-b0f79ee/src/atlas/util/function/VortexRollup.h0000664000175000017500000000241215263665111024144 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the 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 ecmwf-atlas-b0f79ee/src/atlas/util/function/SphericalHarmonic.h0000664000175000017500000000267215263665111025062 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 SphericalHarmonic(int n, int m); /// \brief Evaluate the spherical harmonic function for given longitude and latitude double operator()(double lon, double lat) const; private: struct Cache; double P_cos_COLAT(double lat) const; int n_; int m_; int abs_m_; double Knm_{}; std::shared_ptr cache_; }; } // namespace function } // namespace util } // namespace atlas ecmwf-atlas-b0f79ee/src/atlas/util/MicroDeg.h0000664000175000017500000000135115263665111021324 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/util/UnitSphere.h0000664000175000017500000000155715263665111021731 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/util/GaussianLatitudes.h0000664000175000017500000000367215263665111023274 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/util/GPUClonable.h0000664000175000017500000000257615263665111021740 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/util/Point.cc0000664000175000017500000000171115263665111021062 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/util/Checksum.h0000664000175000017500000000344515263665111021403 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 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); void checksum_reset(checksum_t&); void checksum_update(checksum_t&, const int values[], size_t size); void checksum_update(checksum_t&, const long values[], size_t size); void checksum_update(checksum_t&, const float values[], size_t size); void checksum_update(checksum_t&, const double values[], size_t size); void checksum_update(checksum_t&, const checksum_t values[], size_t size); void checksum_update(checksum_t&, const int values[], size_t size, size_t stride); void checksum_update(checksum_t&, const long values[], size_t size, size_t stride); void checksum_update(checksum_t&, const float values[], size_t size, size_t stride); void checksum_update(checksum_t&, const double values[], size_t size, size_t stride); void checksum_update(checksum_t&, const checksum_t values[], size_t size, size_t stride); checksum_t checksum_digest(const checksum_t&); std::string checksum_to_hex_str(const util::checksum_t& digest); } // namespace util } // namespace atlas ecmwf-atlas-b0f79ee/src/atlas/util/SphericalPolygon.h0000664000175000017500000000240515263665111023116 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/util/PolygonXY.h0000664000175000017500000000371015263665111021544 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/util/QhullSphericalTriangulation.cc0000664000175000017500000001120215263665111025446 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 ecmwf-atlas-b0f79ee/src/atlas/util/Bitflags.h0000664000175000017500000000765415263665111021402 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/util/Config.cc0000664000175000017500000002736615263665111021214 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/util/CoordinateEnums.h0000664000175000017500000000170215263665111022732 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the 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 ecmwf-atlas-b0f79ee/src/atlas/util/Polygon.cc0000664000175000017500000002354615263665111021432 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 //------------------------------------------------------------------------------------------------------ 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 ecmwf-atlas-b0f79ee/src/atlas/util/Metadata.cc0000664000175000017500000002332015263665111021511 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 #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; } std::string Metadata::json(eckit::JSON::Formatting formatting) const { std::stringstream json; eckit::JSON js(json,formatting); json.precision(std::numeric_limits::max_digits10); js << *this; return json.str(); } 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) { broadcast(dest, root, mpi::comm().name()); } void Metadata::broadcast(Metadata& dest, idx_t root, const std::string& mpi_comm) { const auto& comm = mpi::comm(mpi_comm); std::string buffer; int buffer_size{0}; if (comm.rank() == root) { buffer = json(eckit::JSON::Formatting::COMPACT); buffer_size = static_cast(buffer.size()); } ATLAS_TRACE_MPI(BROADCAST) { comm.broadcast(buffer_size, root); } if (comm.rank() != root) { buffer.resize(buffer_size); } ATLAS_TRACE_MPI(BROADCAST) { comm.broadcast(buffer.begin(), buffer.end(), root); } if (not(&dest == this && comm.rank() == root)) { std::stringstream s; s << buffer; eckit::JSONParser parser(s); dest = Metadata(parser.parse()); } } void Metadata::broadcast(Metadata& dest) const { const_cast(this)->broadcast(dest); } void Metadata::broadcast(Metadata& dest, idx_t root) const { const_cast(this)->broadcast(dest, root); } void Metadata::broadcast(Metadata& dest, idx_t root, const std::string& mpi_comm) const { const_cast(this)->broadcast(dest, root, mpi_comm); } 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::string json_str = This->json(); 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 ecmwf-atlas-b0f79ee/src/atlas/util/vector.h0000664000175000017500000001101515263665111021133 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 >= static_cast(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 >= static_cast(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 ecmwf-atlas-b0f79ee/src/atlas/util/GaussianLatitudes.cc0000664000175000017500000000246215263665111023426 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/util/LonLatMicroDeg.h0000664000175000017500000000557115263665111022446 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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/library/config.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/MicroDeg.h" #include "atlas/util/Point.h" namespace atlas { namespace util { // ------------------------------------------------------------------------------------ // Forward declarations class LonLatMicroDeg; uidx_t unique_lonlat(const LonLatMicroDeg&); // ------------------------------------------------------------------------------------ /// @brief LonLatMicroDegrees /// /// Data is stored and accessed in microdegrees in int type (32 bits per /// coordinate component) /// This structure is used to compare 2 lon-lat points in sorting algorithms /// or to use in sending in MPI buffers using minimal memory, /// The maximum range in degrees is [-2147.48 2147.48] class LonLatMicroDeg { public: // -- Constructors taking already microdegrees LonLatMicroDeg(int lonlat[2]) { p_[LON] = lonlat[LON]; p_[LAT] = lonlat[LAT]; } LonLatMicroDeg(int lon, int lat) { p_[LON] = lon; p_[LAT] = lat; } // -- Constructors taking degrees LonLatMicroDeg(const double& lon, const double& lat) { p_[LON] = microdeg(lon); p_[LAT] = microdeg(lat); } LonLatMicroDeg(const double lonlat[2]) { p_[LON] = microdeg(lonlat[LON]); p_[LAT] = microdeg(lonlat[LAT]); } // LonLatMicroDeg( const array::ArrayView& 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" ecmwf-atlas-b0f79ee/src/atlas/util/Checksum.cc0000664000175000017500000001364015263665111021537 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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/runtime/Exception.h" #include "atlas/util/Checksum.h" namespace atlas { namespace util { namespace { // anonymous template const char* as_bytes(const Value values[], size_t size) { return size ? reinterpret_cast(values) : nullptr; } template size_t byte_size(size_t size) { return size * sizeof(Value) / sizeof(char); } union Fletcher16 { checksum_t checksum; uint16_t c[2]; }; static void fletcher16_reset(Fletcher16& f) { f.c[0] = 0; f.c[1] = 0; } static void fletcher16_update(Fletcher16& f, const uint8_t* data, size_t size) { uint32_t c0 = f.c[0]; uint32_t c1 = f.c[1]; while(size > 0) { size_t blocklen = size; if (blocklen > 5802) { blocklen = 5802; } size -= blocklen; do { c0 = c0 + *data++; c1 = c1 + c0; } while (--blocklen); c0 = c0 % 0xff; c1 = c1 % 0xff; } f.c[0] = c0; f.c[1] = c1; } static void fletcher16_update(Fletcher16& f, const uint8_t* data, size_t size, size_t stride, size_t chunk) { if (stride == 1 && chunk == 1) { fletcher16_update(f, data, size); return; } uint32_t c0 = f.c[0]; uint32_t c1 = f.c[1]; const size_t max_blocklen = std::max(size_t{1}, size_t{5802} / chunk); while (size > 0) { size_t blocklen = size; if (blocklen > max_blocklen) { blocklen = max_blocklen; } size -= blocklen; do { for (size_t byte = 0; byte < chunk; ++byte) { c0 = c0 + data[byte]; c1 = c1 + c0; } data += stride; } while (--blocklen); c0 = c0 % 0xff; c1 = c1 % 0xff; } f.c[0] = c0; f.c[1] = c1; } static uint16_t fletcher16_finish(const Fletcher16& f) { uint32_t c0 = f.c[0]; uint32_t c1 = f.c[1]; return (c1 << 8 | c0); } static uint16_t fletcher16(const uint8_t* data, size_t size) { Fletcher16 checksum; fletcher16_reset(checksum); fletcher16_update(checksum, data, size); return fletcher16_finish(checksum); } static void checksum_update(checksum_t& checksum, const char* data, size_t bytes) { fletcher16_update(reinterpret_cast(checksum), reinterpret_cast(data), bytes); } template static void checksum_update_strided(checksum_t& checksum, const Value values[], size_t size, size_t stride) { auto& f = reinterpret_cast(checksum); const auto* data = reinterpret_cast(values); const size_t value_bytes = sizeof(Value); const size_t stride_bytes = stride * value_bytes; fletcher16_update(f, data, size, stride_bytes, value_bytes); } static checksum_t checksum(const char* data, size_t size) { return fletcher16(reinterpret_cast(data), size / sizeof(uint8_t)); } } // namespace void checksum_reset(checksum_t& checksum) { fletcher16_reset(reinterpret_cast(checksum)); } checksum_t checksum(const int values[], size_t size) { return checksum(as_bytes(values, size), byte_size(size)); } checksum_t checksum(const long values[], size_t size) { return checksum(as_bytes(values, size), byte_size(size)); } checksum_t checksum(const float values[], size_t size) { return checksum(as_bytes(values, size), byte_size(size)); } checksum_t checksum(const double values[], size_t size) { return checksum(as_bytes(values, size), byte_size(size)); } checksum_t checksum(const checksum_t values[], size_t size) { return checksum(as_bytes(values, size), byte_size(size)); } void checksum_update(checksum_t& c, const int values[], size_t size) { checksum_update(c, as_bytes(values, size), byte_size(size)); } void checksum_update(checksum_t& c, const long values[], size_t size) { checksum_update(c, as_bytes(values, size), byte_size(size)); } void checksum_update(checksum_t& c, const float values[], size_t size) { checksum_update(c, as_bytes(values, size), byte_size(size)); } void checksum_update(checksum_t& c, const double values[], size_t size) { checksum_update(c, as_bytes(values, size), byte_size(size)); } void checksum_update(checksum_t& c, const checksum_t values[], size_t size) { checksum_update(c, as_bytes(values, size), byte_size(size)); } void checksum_update(checksum_t& c, const int values[], size_t size, size_t stride) { checksum_update_strided(c, values, size, stride); } void checksum_update(checksum_t& c, const long values[], size_t size, size_t stride) { checksum_update_strided(c, values, size, stride); } void checksum_update(checksum_t& c, const float values[], size_t size, size_t stride) { checksum_update_strided(c, values, size, stride); } void checksum_update(checksum_t& c, const double values[], size_t size, size_t stride) { checksum_update_strided(c, values, size, stride); } void checksum_update(checksum_t& c, const checksum_t values[], size_t size, size_t stride) { checksum_update_strided(c, values, size, stride); } checksum_t checksum_digest(const checksum_t& checksum) { return fletcher16_finish(reinterpret_cast(checksum)); } namespace { inline std::string to_hex_str(util::checksum_t digest) { // Configure the stream for hex, lowercase, and zero padding std::stringstream ss; ss << std::hex << std::nouppercase << std::setw(4) << std::setfill('0') << digest; return ss.str(); } } std::string checksum_to_hex_str(const util::checksum_t& digest) { return to_hex_str(digest); } } // namespace util } // namespace atlas ecmwf-atlas-b0f79ee/src/atlas/util/VectorOfAbstract.h0000664000175000017500000000567715263665111023065 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/atlas/util/Geometry.h0000664000175000017500000001777515263665111021447 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/util/Geometry.cc0000664000175000017500000001267615263665111021600 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/atlas/util/PackVectorFields.cc0000664000175000017500000001650315263665111023166 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 ecmwf-atlas-b0f79ee/src/atlas/util/GridPointsJSONWriter.cc0000664000175000017500000002710715263665111023751 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 #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; } virtual bool get(const std::string& name, std::vector& value) const { return false; } }; } // namespace util } // namespace atlas ecmwf-atlas-b0f79ee/src/atlas/util/Registry.h0000664000175000017500000000317015263665111021444 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 ecmwf-atlas-b0f79ee/src/atlas/util/Point.h0000664000175000017500000001206415263665111020727 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the 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 ecmwf-atlas-b0f79ee/src/atlas/util/Factory.h0000664000175000017500000000702515263665111021246 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/util/PolygonLocator.h0000664000175000017500000001353315263665111022613 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/atlas/projection.h0000664000175000017500000000073515263665111021037 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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" ecmwf-atlas-b0f79ee/src/apps/0000775000175000017500000000000015263665111016344 5ustar alastairalastairecmwf-atlas-b0f79ee/src/apps/CMakeLists.txt0000664000175000017500000000211315263665111021101 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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) ecmwf-atlas-b0f79ee/src/apps/atlas.cc0000664000175000017500000000653115263665111017764 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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(); } ecmwf-atlas-b0f79ee/src/apps/atlas-grids.cc0000664000175000017500000007067715263665111021106 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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(); } ecmwf-atlas-b0f79ee/src/apps/atlas-gaussian-latitudes.cc0000664000175000017500000001041315263665111023562 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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(); } ecmwf-atlas-b0f79ee/src/apps/atlas-interpolations/0000775000175000017500000000000015263665111022520 5ustar alastairalastairecmwf-atlas-b0f79ee/src/apps/atlas-interpolations/atlas-interpolations.cc0000664000175000017500000010325715263665111027213 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(); } ecmwf-atlas-b0f79ee/src/apps/atlas-interpolations/CMakeLists.txt0000664000175000017500000001266315263665111025270 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()ecmwf-atlas-b0f79ee/src/apps/atlas-interpolations/ScripIO.cc0000664000175000017500000000771615263665111024352 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 } } ecmwf-atlas-b0f79ee/src/apps/atlas-interpolations/ScripIO.h0000664000175000017500000000134515263665111024204 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&); }; } ecmwf-atlas-b0f79ee/src/apps/atlas-meshgen.cc0000664000175000017500000003646715263665111021423 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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("gmsh-format", "Gmsh output format [ascii,binary] (default=ascii)")); add_option(new SimpleOption("gmsh-gather", "Gather Gmsh field output to rank 0 (default=false)")); add_option(new SimpleOption( "gmsh-partition-as-entity", "Use the element partition as the Gmsh elementary entity (default=false)")); 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)) { std::string gmsh_format = "ascii"; args.get("gmsh-format", gmsh_format); if (gmsh_format != "ascii" && gmsh_format != "binary") { throw_Exception("Invalid --gmsh-format='" + gmsh_format + "': expected 'ascii' or 'binary'", Here()); } 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("binary", gmsh_format == "binary"); gmsh_config.set("coordinates", coordinates); if (edges) { gmsh_config.set("elements", "edges"); } gmsh_config.set("gather", args.getBool("gmsh-gather", false)); gmsh_config.set("ghost", ghost); gmsh_config.set("info", info); gmsh_config.set("element_partition_as_entity", args.getBool("gmsh-partition-as-entity", false)); 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(); } ecmwf-atlas-b0f79ee/src/apps/atlas-grid-points.cc0000664000175000017500000001126515263665111022221 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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(); } ecmwf-atlas-b0f79ee/src/sandbox/0000775000175000017500000000000015263665111017037 5ustar alastairalastairecmwf-atlas-b0f79ee/src/sandbox/benchmark_sorting/0000775000175000017500000000000015263665111022536 5ustar alastairalastairecmwf-atlas-b0f79ee/src/sandbox/benchmark_sorting/CMakeLists.txt0000664000175000017500000000100115263665111025266 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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 ) ecmwf-atlas-b0f79ee/src/sandbox/benchmark_sorting/atlas-benchmark-sorting.cc0000664000175000017500000002076315263665111027574 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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(); } ecmwf-atlas-b0f79ee/src/sandbox/benchmark_ifs_setup/0000775000175000017500000000000015263665111023052 5ustar alastairalastairecmwf-atlas-b0f79ee/src/sandbox/benchmark_ifs_setup/CMakeLists.txt0000664000175000017500000000105515263665111025613 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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 ) ecmwf-atlas-b0f79ee/src/sandbox/benchmark_ifs_setup/atlas-benchmark-ifs-setup.cc0000664000175000017500000000753515263665111030344 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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(); } ecmwf-atlas-b0f79ee/src/sandbox/interpolation-fortran/0000775000175000017500000000000015263665111023377 5ustar alastairalastairecmwf-atlas-b0f79ee/src/sandbox/interpolation-fortran/CMakeLists.txt0000664000175000017500000000110515263665111026134 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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() ecmwf-atlas-b0f79ee/src/sandbox/interpolation-fortran/atlas-interpolation-fortran.F900000664000175000017500000001073615263665111031330 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/sandbox/interpolation/0000775000175000017500000000000015263665111021726 5ustar alastairalastairecmwf-atlas-b0f79ee/src/sandbox/interpolation/atlas-parallel-structured-interpolation.cc0000664000175000017500000001734415263665111032233 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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(); } ecmwf-atlas-b0f79ee/src/sandbox/interpolation/CMakeLists.txt0000664000175000017500000000302715263665111024470 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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() ecmwf-atlas-b0f79ee/src/sandbox/interpolation/atlas-global-matrix.cc0000664000175000017500000004353615263665111026114 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(); } ecmwf-atlas-b0f79ee/src/sandbox/interpolation/atlas-parallel-interpolation.cc0000664000175000017500000003326315263665111030027 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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(); } ecmwf-atlas-b0f79ee/src/sandbox/interpolation/PartitionedMesh.cc0000664000175000017500000000527415263665111025344 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/sandbox/interpolation/atlas-conservative-interpolation.cc0000664000175000017500000003655715263665111030754 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); util::function::SphericalHarmonic Y(n, m); 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); atlas_omp_parallel_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(); } ecmwf-atlas-b0f79ee/src/sandbox/interpolation/ScripIO.cc0000664000175000017500000000771615263665111023560 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 } } ecmwf-atlas-b0f79ee/src/sandbox/interpolation/PartitionedMesh.h0000664000175000017500000000252415263665111025201 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges 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 ecmwf-atlas-b0f79ee/src/sandbox/interpolation/ScripIO.h0000664000175000017500000000134515263665111023412 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&); }; } ecmwf-atlas-b0f79ee/src/sandbox/benchmark_trans/0000775000175000017500000000000015263665111022200 5ustar alastairalastairecmwf-atlas-b0f79ee/src/sandbox/benchmark_trans/atlas-benchmark-trans.cc0000664000175000017500000002734715263665111026705 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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(); } ecmwf-atlas-b0f79ee/src/sandbox/benchmark_trans/CMakeLists.txt0000664000175000017500000000077515263665111024751 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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 ) ecmwf-atlas-b0f79ee/src/sandbox/CMakeLists.txt0000664000175000017500000000163215263665111021601 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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 ) ecmwf-atlas-b0f79ee/src/sandbox/fortran_submodule/0000775000175000017500000000000015263665111022571 5ustar alastairalastairecmwf-atlas-b0f79ee/src/sandbox/fortran_submodule/CMakeLists.txt0000664000175000017500000000101615263665111025327 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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() ecmwf-atlas-b0f79ee/src/sandbox/fortran_submodule/sb_module.F900000664000175000017500000000100315263665111025014 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/sandbox/grid_distribution/0000775000175000017500000000000015263665111022563 5ustar alastairalastairecmwf-atlas-b0f79ee/src/sandbox/grid_distribution/CMakeLists.txt0000664000175000017500000000100015263665111025312 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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 ) ecmwf-atlas-b0f79ee/src/sandbox/grid_distribution/atlas-grid-distribution.cc0000664000175000017500000001375215263665111027646 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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(); } ecmwf-atlas-b0f79ee/src/sandbox/fortran_acc_fields/0000775000175000017500000000000015263665111022646 5ustar alastairalastairecmwf-atlas-b0f79ee/src/sandbox/fortran_acc_fields/CMakeLists.txt0000664000175000017500000000114315263665111025405 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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() ecmwf-atlas-b0f79ee/src/sandbox/fortran_acc_fields/atlas-acc-fields.F900000664000175000017500000000234715263665111026230 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/sandbox/example_fortran/0000775000175000017500000000000015263665111022225 5ustar alastairalastairecmwf-atlas-b0f79ee/src/sandbox/example_fortran/example_fortran.F900000664000175000017500000000164115263665111025675 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive 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 ecmwf-atlas-b0f79ee/src/sandbox/example_fortran/CMakeLists.txt0000664000175000017500000000102515263665111024763 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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() ecmwf-atlas-b0f79ee/src/sandbox/fortran_object/0000775000175000017500000000000015263665111022040 5ustar alastairalastairecmwf-atlas-b0f79ee/src/sandbox/fortran_object/CMakeLists.txt0000664000175000017500000000100315263665111024572 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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() ecmwf-atlas-b0f79ee/src/sandbox/fortran_object/fortran_object.F900000664000175000017500000000407615263665111025330 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/sandbox/apps/0000775000175000017500000000000015263665111020002 5ustar alastairalastairecmwf-atlas-b0f79ee/src/sandbox/apps/atlas-loadbalance.cc0000664000175000017500000001303415263665111023641 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive 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(); } ecmwf-atlas-b0f79ee/src/sandbox/apps/atlas-gmsh-extract.cc0000664000175000017500000002103415263665111024021 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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(); } ecmwf-atlas-b0f79ee/src/sandbox/apps/CMakeLists.txt0000664000175000017500000000162715263665111022550 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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 ) ecmwf-atlas-b0f79ee/src/sandbox/apps/atlas-numerics-nabla.F900000664000175000017500000002471315263665111024273 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not 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 ecmwf-atlas-b0f79ee/src/sandbox/apps/atlas-benchmark.cc0000664000175000017500000005177715263665111023366 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not 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(grad_field) << 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(); } ecmwf-atlas-b0f79ee/doc/0000775000175000017500000000000015263665111015357 5ustar alastairalastairecmwf-atlas-b0f79ee/doc/Doxyfile-custom0000664000175000017500000000251415263665111020377 0ustar alastairalastair@INCLUDE = Doxyfile-default # Nested folders will be ignored without this. You may not need it. RECURSIVE = YES # Enable preprocessing and related preprocessor necessities ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = YES SKIP_FUNCTION_MACROS = NO # extra defs for to help with building the _right_ version of the docs PREDEFINED = DOXYGEN_DOCUMENTATION_BUILD PREDEFINED += DOXYGEN_SHOULD_SKIP_THIS # Using `=` instead of `+=` overrides HIDE_FRIEND_COMPOUNDS = YES PREDEFINED += protected=private PREDEFINED += DOXYGEN_HIDE(X)= PREDEFINED += ATLAS_HOST_DEVICE= PREDEFINED += INDEX_REF=idx_t& EXTRACT_PRIVATE = NO EXCLUDE_PATTERNS = */detail/* EXCLUDE_PATTERNS += detail/* EXCLUDE_PATTERNS += */test/* EXCLUDE_PATTERNS += */gridtools/* EXCLUDE_PATTERNS += */native/* EXCLUDE_PATTERNS += */helpers/* EXCLUDE_PATTERNS += */Object.h EXCLUDE_PATTERNS += */ObjectHandle.h EXCLUDE_PATTERNS += */State.h EXCLUDE_PATTERNS += *.cc EXCLUDE_PATTERNS += CMakeLists.txt EXCLUDE_PATTERNS += */TableView.h EXCLUDE_PATTERNS += */Table.h EXCLUDE_SYMBOLS = detail EXCLUDE_SYMBOLS += eckit INLINE_INHERITED_MEMB = YES HIDE_UNDOC_RELATIONS = NO GENERATE_TODOLIST = NO GENERATE_BUGLIST = NO #BUILTIN_STL_SUPPORT = NO TOC_INCLUDE_HEADINGS = 5 MARKDOWN_SUPPORT = YES ecmwf-atlas-b0f79ee/doc/Doxyfile.in0000664000175000017500000000110415263665111017466 0ustar alastairalastair@INCLUDE = @PROJECT_SOURCE_DIR@/doc/@ATLAS_DOXYFILE@ INPUT = @PROJECT_SOURCE_DIR@/src/atlas INPUT += @PROJECT_SOURCE_DIR@/doc/pages OUTPUT_DIRECTORY = @PROJECT_BINARY_DIR@/doc/build STRIP_FROM_PATH = @PROJECT_SOURCE_DIR@/src STRIP_FROM_PATH += @PROJECT_SOURCE_DIR@/doc MARKDOWN_SUPPORT = YES PROJECT_NAME = Atlas PROJECT_NUMBER = @ATLAS_DOC_VERSION@ PROJECT_BRIEF = "C++ docs » @ATLAS_DOC_VERSION@" ALIASES += project_version=@ATLAS_DOC_VERSION@ DOTFILE_DIRS = @PROJECT_SOURCE_DIR@/doc/pages ecmwf-atlas-b0f79ee/doc/CMakeLists.txt0000664000175000017500000000532015263665111020117 0ustar alastairalastair# (C) Copyright 2013 ECMWF. # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # In applying this licence, ECMWF does not 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_DOCS) if(atlas_HAVE_FORTRAN) add_subdirectory(example-fortran) endif() add_subdirectory(user-guide) list( APPEND DOC_CODE_TARGETS atlas_c-hello-world atlas_c-global-grids-Structured atlas_c-global-grids-Unstructured atlas_c-meshes-Structured atlas_c-fields atlas_c-fields-on-grid atlas_c-NodeColumns atlas_c-StructuredColumns ) if( atlas_HAVE_FORTRAN ) list( APPEND DOC_CODE_TARGETS atlas_f-hello-world atlas_f-global-grids-Structured atlas_f-meshes-Structured atlas_f-fields atlas_f-fields-on-grid atlas_f-NodeColumns ) endif() add_custom_target(atlas_doc_code) add_dependencies (atlas_doc_code ${DOC_CODE_TARGETS} ) add_custom_target(atlas_doc_pdf) add_dependencies (atlas_doc_pdf atlas-user-guide-pdf ) if( LATEX_HTLATEX_FOUND ) add_custom_target(atlas_doc_html) add_dependencies (atlas_doc_html atlas-user-guide-html ) endif() add_custom_target(atlas_doc) add_dependencies (atlas_doc atlas_doc_code atlas_doc_pdf ) if( PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME ) add_custom_target(doc) add_dependencies(doc atlas_doc) endif() if( NOT DEFINED ATLAS_DOC_VERSION ) set( ATLAS_DOC_VERSION ${ATLAS_VERSION_STR} ) endif() if( NOT DEFINED ATLAS_DOXYGEN_GENERATOR ) set( ATLAS_DOXYGEN_GENERATOR "stock") endif() if( ATLAS_DOXYGEN_GENERATOR STREQUAL "m.css" ) set( ATLAS_DOXYFILE Doxyfile-mcss ) set( ATLAS_DOXYGEN_EXECUTABLE doxygen.py ) else() set( ATLAS_DOXYFILE Doxyfile-stock ) endif() if( NOT DEFINED ATLAS_DOXYGEN_EXECUTABLE ) find_package(Doxygen COMPONENTS dot) if( DOXYGEN_FOUND ) set( ATLAS_DOXYGEN_EXECUTABLE ${DOXYGEN_EXECUTABLE} ) else() set( ATLAS_DOXYGEN_EXECUTABLE doxygen ) endif() endif() foreach( doxyfile Doxyfile-default Doxyfile-custom Doxyfile-mcss Doxyfile-stock ) execute_process( COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/${doxyfile} ${CMAKE_CURRENT_BINARY_DIR}/${doxyfile} ) endforeach() configure_file(Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) add_custom_target( atlas_doxygen COMMAND ${ATLAS_DOXYGEN_EXECUTABLE} Doxyfile WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generating Doxygen documentation" VERBATIM ) set_property(TARGET atlas_doxygen PROPERTY EXCLUDE_FROM_ALL TRUE) endif() ecmwf-atlas-b0f79ee/doc/example-fortran/0000775000175000017500000000000015263665111020463 5ustar alastairalastairecmwf-atlas-b0f79ee/doc/example-fortran/CMakeLists.txt0000664000175000017500000000064715263665111023232 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. add_subdirectory(structured-grid-stencils) ecmwf-atlas-b0f79ee/doc/example-fortran/structured-grid-stencils/0000775000175000017500000000000015263665111025434 5ustar alastairalastairecmwf-atlas-b0f79ee/doc/example-fortran/structured-grid-stencils/CMakeLists.txt0000664000175000017500000000111415263665111030171 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_executable( TARGET example-fortran-structured-grid-stencils OUTPUT_NAME program NOINSTALL SOURCES program.F90 LIBS atlas_f ) ecmwf-atlas-b0f79ee/doc/example-fortran/structured-grid-stencils/program.F900000664000175000017500000001704215263665111027367 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. ! ------------------------------------------------------------------------------------------------ ! Example program on how to access and use StructuredColumns functionspace ! Also includes computation of stencils around a given coordinate ! ------------------------------------------------------------------------------------------------ subroutine run use, intrinsic :: iso_fortran_env, only : real64, real32 use atlas_module, only : & & atlas_ReducedGaussianGrid ,& & atlas_functionspace_StructuredColumns ,& & atlas_Field, & & ATLAS_KIND_IDX, & & atlas_StructuredGrid_ComputeNorth ,& & atlas_StructuredGrid_ComputeWest ,& & atlas_StructuredGrid_ComputeStencil ,& & atlas_StructuredGrid_Stencil, & & atlas_real use fckit_mpi_module, only : fckit_mpi ! ------------------------------------------------ implicit none character(len=:), allocatable :: gridname type(atlas_ReducedGaussianGrid) :: grid type(atlas_functionspace_StructuredColumns) :: functionspace real(real64) :: zlon, zlat integer(ATLAS_KIND_IDX) :: jlon,jlat, jgp real(real64), pointer :: xy(:,:) integer :: log_rank ! ------------------------------------------------ log_rank = 1 ! First partition has rank=0 !!! if (fckit_mpi%size() == 1) then log_rank = 0 endif gridname = "O320" ! Create grid and write some properties !grid = atlas_StructuredGrid(gridname) grid = atlas_ReducedGaussianGrid([20,24,28,32,36,40,44,48,52,56,60,64,& 64,60,56,52,48,44,40,36,32,28,24,20]) if (fckit_mpi%rank() == log_rank) then write(0,*) "grid%name() : ", grid%name() write(0,*) "grid%size() : ", grid%size() write(0,*) "grid%ny() : ", grid%ny() write(0,*) "grid%nxmax() : ", grid%nxmax() endif ! Create functionspace of grid, distributed across MPI tasks with default partitioner, and with halo of 2 wide ! The default partitioner is "equal_regions" functionspace = atlas_functionspace_StructuredColumns(grid, halo=2) ! Latitude bounds without and with halos of this partition if (fckit_mpi%rank() == log_rank) then write(0,*) "functionspace%size_owned() : ", functionspace%size_owned() write(0,*) "functionspace%size() : ", functionspace%size() write(0,'(A,I0,A,I0)') " halo points between index ", functionspace%size_owned()+1 , " and " , functionspace%size() write(0,*) "functionspace%j_begin(), functionspace%j_end() : ", & & functionspace%j_begin(), functionspace%j_end() write(0,*) "functionspace%j_begin_halo(), functionspace%j_end_halo() : ", & & functionspace%j_begin_halo(), functionspace%j_end_halo() endif ! Longitude bounds without and with halos of this partition for the first latitude jlat = functionspace%j_begin() if (fckit_mpi%rank() == log_rank) then write(0,*) "functionspace%i_begin(jlat), functionspace%i_end(jlat) : ", functionspace%i_begin(jlat), & & functionspace%i_end(jlat) write(0,*) "functionspace%i_begin_halo(jlat), functionspace%i_end_halo(jlat) : ", functionspace%i_begin_halo(jlat),& & functionspace%i_end_halo(jlat) endif ! Access i,j indices of grid points block type(atlas_Field) :: field_index_i, field_index_j integer(ATLAS_KIND_IDX), pointer :: index_i(:), index_j(:) field_index_i = functionspace%index_i() field_index_j = functionspace%index_j() call field_index_i%data(index_i) call field_index_j%data(index_j) if (fckit_mpi%rank() == log_rank) then write(0,*) "i,j of first partition point :", index_i(1), index_j(1) endif call field_index_i%final() call field_index_j%final() end block ! Access to xy coordinates block type(atlas_Field) :: field_xy field_xy = functionspace%xy() call field_xy%data(xy) call field_xy%final() end block ! Creating a horizontal field and perform halo exchange block type(atlas_Field) :: field real(real32), pointer :: view(:) field = functionspace%create_field(name="myfield", kind=atlas_real(real32)) call field%data(view) if (fckit_mpi%rank() == log_rank) then write(0,*) "shape( horizontal field )", shape(view) endif view(1:functionspace%size_owned()) = 1. view(functionspace%size_owned()+1:functionspace%size()) = 0 call field%set_dirty() ! This marks that halos are in "dirty" state call field%halo_exchange() ! Only exchanges halos when halos are marked as "dirty" if (fckit_mpi%rank() == log_rank) then write(0,*) "halo exhange success : ", all(view == 1.) endif call field%final() end block ! Creating a horizontal/vertical field block type(atlas_Field) :: field real(real32), pointer :: view(:,:) field = functionspace%create_field(name="myfield", kind=atlas_real(real32), levels=10) call field%data(view) if (fckit_mpi%rank() == log_rank) then write(0,*) "shape( horizontal/vertical field )", shape(view) endif call field%final() end block ! Set a coordinate somewhere south-east of the first grid point, but still within this partition jgp = 1 zlon = xy(1,jgp) + 0.1 zlat = xy(2,jgp) - 0.1 ! Compute nearest points to the north-west of a coordinate block type(atlas_StructuredGrid_ComputeNorth) :: compute_north type(atlas_StructuredGrid_ComputeWest) :: compute_west compute_north = atlas_StructuredGrid_ComputeNorth(grid, halo=2) compute_west = atlas_StructuredGrid_ComputeWest(grid, halo=2) jlat = compute_north%execute(zlat) jlon = compute_west%execute(zlon, jlat) jgp = functionspace%index(jlon,jlat) ! gridpoint index in 1-dimensional array if (fckit_mpi%rank() == log_rank) then write(0,'(A,F6.2,A,I0)') "compute_north%execute(y=",zlat,") : ", jlat write(0,'(A,F6.2,A,I0,A,I0)') "compute_west%execute(x=",zlon,", j=", jlat,") : ", jlon write(0,'(A,I0,A,F6.2,A,F6.2,A)') "gridpoint north-west: jpg=",jgp," (lon,lat)=(", xy(1,jgp), ",", xy(2,jgp), ")" endif call compute_west%final() call compute_north%final() end block ! Stencil computer of 4x4 stencil around a coordinate block type(atlas_StructuredGrid_ComputeStencil) :: compute_stencil type(atlas_StructuredGrid_Stencil) :: stencil call compute_stencil%setup(grid, stencil_width=4) call compute_stencil%execute(zlon,zlat,stencil) if (fckit_mpi%rank() == log_rank) then write(0,'(A,A,dt)') 'stencil:', new_line('a'), stencil endif if (fckit_mpi%rank() == log_rank) then write(0,'(A,A,dt)') 'stencil gridpoints:' do jlat=1,stencil%width do jlon=1,stencil%width write(0,'(I8)',advance='no') functionspace%index(stencil%i(jlon,jlat),stencil%j(jlat)) enddo write(0,'(A)') "" enddo write(0,'(A,A,dt)') 'stencil coordinates:' do jlat=1,stencil%width jgp = functionspace%index(stencil%i(1,jlat),stencil%j(jlat)) write(0,'(A, F6.2,A)', advance='no') " lat : ", xy(2,jgp), " --- lon : " do jlon=1,stencil%width jgp = functionspace%index(stencil%i(jlon,jlat),stencil%j(jlat)) write(0,'(F8.2)',advance='no') xy(1,jgp) enddo write(0,'(A)') "" enddo endif call compute_stencil%final() end block call functionspace%final() call grid%final() end subroutine ! ------------------------------------------------------------------------------------------------ program main use atlas_module, only : atlas_library implicit none call atlas_library%initialize() call run() call atlas_library%finalize() end programecmwf-atlas-b0f79ee/doc/Doxyfile-default0000664000175000017500000033030415263665111020512 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 # , /