diff --git a/.github/workflows/Dockerfile.gnu b/.github/workflows/Dockerfile.gnu new file mode 100644 index 0000000000..3506c2b9ee --- /dev/null +++ b/.github/workflows/Dockerfile.gnu @@ -0,0 +1,68 @@ +#*********************************************************************** +#* GNU Lesser General Public License +#* +#* This file is part of the GFDL Flexible Modeling System (FMS). +#* +#* FMS is free software: you can redistribute it and/or modify it under +#* the terms of the GNU Lesser General Public License as published by +#* the Free Software Foundation, either version 3 of the License, or (at +#* your option) any later version. +#* +#* FMS is distributed in the hope that it will be useful, but WITHOUT +#* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +#* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +#* for more details. +#* +#* You should have received a copy of the GNU Lesser General Public +#* License along with FMS. If not, see . +#*********************************************************************** +# FMS CI image recipefile for GNU +# Runs on centos stream (builder has same base from redhat registry) +# +# arguments to specify versions to build can be given to docker or changed here (--build-arg name=val) +FROM spack/rockylinux9:latest as builder + +ARG gcc_version=12.3.0 +ARG netcdfc_version=4.9.0 +ARG netcdff_version=4.6.0 +ARG libyaml_version=0.2.5 +ARG mpich_version=4.0.2 + +COPY spack.env /opt/deps/spack.env + +# perl's download kept timing out +RUN sed -i 's/connect_timeout: 10/connect_timeout: 600/' /opt/spack/etc/spack/defaults/config.yaml && \ + spack install gcc@${gcc_version} && \ + source /opt/spack/share/spack/setup-env.sh && \ + spack load gcc@${gcc_version} && \ + spack compiler find && \ + sed "s/COMPILER/gcc@$gcc_version/" /opt/deps/spack.env > spack.yaml && \ + sed -i "s/NETCDF_C_VERSION/$netcdfc_version/" spack.yaml && \ + sed -i "s/NETCDF_F_VERSION/$netcdff_version/" spack.yaml && \ + sed -i "s/LIBYAML_VERSION/$libyaml_version/" spack.yaml && \ + sed -i "s/MPI_LIB/mpich@$mpich_version/" spack.yaml && \ + spack env activate -d . && \ + spack -e . concretize -f > /opt/deps/deps.log && \ + spack install --fail-fast + +# copy built software to base from first image +FROM rockylinux:9 + +COPY --from=builder /opt/view/ /opt/view/ +COPY --from=builder /opt/deps/ /opt/deps/ + +# input files used with --enable-input-tests +# need to be on the dev boxes if building +COPY ./fms_test_input /home/unit_tests_input + +RUN dnf install -y autoconf make automake m4 libtool pkg-config zip + +ENV FC="mpifort" +ENV CC="mpicc" +ENV MPICH_FC="/opt/view/bin/gfortran" +ENV MPICH_CC="/opt/view/bin/gcc" +ENV FCFLAGS="-I/opt/view/include" +ENV CFLAGS="-I/opt/view/include" +ENV LDFLAGS="-L/opt/view/lib" +ENV LD_LIBRARY_PATH="/opt/view/lib:/opt/view/lib64:/usr/local/lib:/usr/local/lib64" +ENV PATH="/opt/view/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin" diff --git a/.github/workflows/github_autotools_gnu.yml b/.github/workflows/github_autotools_gnu.yml new file mode 100644 index 0000000000..b7008a0814 --- /dev/null +++ b/.github/workflows/github_autotools_gnu.yml @@ -0,0 +1,39 @@ +# 'main' required ci, does a distcheck (builds, tests, check install) +# image created off dockerfile in repo, compile/link flags are set there +name: Build libFMS test with autotools + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + conf-flag: [ --disable-openmp, --enable-mixed-mode, --disable-setting-flags, --with-mpi=no] + input-flag: [--with-yaml, --enable-test-input=/home/unit_tests_input] + exclude: + - conf-flag: --with-mpi=no + input-flag: --enable-test-input=/home/unit_tests_input + container: + image: noaagfdl/fms-ci-rocky-gnu:12.3.0 + env: + TEST_VERBOSE: 1 + DISTCHECK_CONFIGURE_FLAGS: "${{ matrix.conf-flag }} ${{ matrix.input-flag }} ${{ matrix.io-flag }}" + SKIP_TESTS: "test_yaml_parser.5" # temporary till fixes are in + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Prepare GNU autoconf for build + run: autoreconf -if + - name: Configure the build + if: ${{ matrix.conf-flag != '--disable-setting-flags' }} + run: ./configure ${DISTCHECK_CONFIGURE_FLAGS} || cat config.log + - name: Configure the build with compiler flags + if: ${{ matrix.conf-flag == '--disable-setting-flags' }} + run: ./configure ${DISTCHECK_CONFIGURE_FLAGS} FCFLAGS="-fdefault-real-8 -fdefault-double-8 -fcray-pointer -ffree-line-length-none -I/usr/include $FCFLAGS" || cat config.log + - name: Build the library + run: make distcheck + if: ${{ matrix.conf-flag != '--with-mpi=no' }} + - name: Build the library (without test suite for serial build) + run: make + if: ${{ matrix.conf-flag == '--with-mpi=no' }} diff --git a/.github/workflows/intel_pr.yml b/.github/workflows/github_autotools_intel.yml similarity index 92% rename from .github/workflows/intel_pr.yml rename to .github/workflows/github_autotools_intel.yml index 62a15361ea..851ad71520 100644 --- a/.github/workflows/intel_pr.yml +++ b/.github/workflows/github_autotools_intel.yml @@ -2,13 +2,16 @@ on: pull_request jobs: intel-autotools: runs-on: ubuntu-latest + strategy: + matrix: + io-flag: ["--disable-deprecated-io", "--enable-deprecated-io"] container: image: intel/oneapi-hpckit:2023.1.0-devel-ubuntu20.04 env: CC: mpiicc FC: mpiifort CFLAGS: "-I/libs/include" - FCFLAGS: "-I/libs/include -g -traceback" + FCFLAGS: "-I/libs/include -g -traceback ${{ matrix.io-flag }}" LDFLAGS: "-L/libs/lib" TEST_VERBOSE: 1 I_MPI_FABRICS: "shm" # needed for mpi in image diff --git a/.github/workflows/build_cmake_gnu.yml b/.github/workflows/github_cmake_gnu.yml similarity index 100% rename from .github/workflows/build_cmake_gnu.yml rename to .github/workflows/github_cmake_gnu.yml diff --git a/.github/workflows/coupler.yml b/.github/workflows/github_coupler_gnu.yml similarity index 100% rename from .github/workflows/coupler.yml rename to .github/workflows/github_coupler_gnu.yml diff --git a/.github/workflows/update_docs.yml b/.github/workflows/github_doc_site.yml similarity index 100% rename from .github/workflows/update_docs.yml rename to .github/workflows/github_doc_site.yml diff --git a/.github/workflows/lint_fms.yml b/.github/workflows/github_linter.yml similarity index 100% rename from .github/workflows/lint_fms.yml rename to .github/workflows/github_linter.yml diff --git a/.github/workflows/am4_regression_parallelWorks_intel_tag.yml b/.github/workflows/parallelworks_am4_intel.yml similarity index 100% rename from .github/workflows/am4_regression_parallelWorks_intel_tag.yml rename to .github/workflows/parallelworks_am4_intel.yml diff --git a/.github/workflows/spack.env b/.github/workflows/spack.env new file mode 100644 index 0000000000..69a3bdcbd0 --- /dev/null +++ b/.github/workflows/spack.env @@ -0,0 +1,17 @@ +# template for spack environment yaml +# uppercase words get replaced before activating +spack: + specs: + - COMPILER + - MPI_LIB + - netcdf-c@NETCDF_C_VERSION ^MPI_LIB + - netcdf-fortran@NETCDF_F_VERSION + - libyaml@LIBYAML_VERSION + concretizer: + unify: true + packages: + all: + compiler: [ COMPILER ] + config: + install_tree: /opt/deps + view: /opt/view diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index 1e71236386..31641aea69 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -1,3 +1,5 @@ +# appends -dev to the version upon release and opens pr +# CI won't run on generated PR, easiest workaround is to close + reopen on: release: types: [published] @@ -16,4 +18,4 @@ jobs: branch-suffix: timestamp # add a timestamp to branch name delete-branch: true # delete afer merge title: Append dev to version number post-release - body: automated change, adds '-dev' to the version number upon releases + body: automated change, adds '-dev' to the version number upon releases. This PR will need to be closed and reopened to run CI testing. diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cc9802f8f..2c616e647d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,45 @@ and this project uses `yyyy.rr[.pp]`, where `yyyy` is the year a patch is releas `rr` is a sequential release number (starting from `01`), and an optional two-digit sequential patch number (starting from `01`). +## [2023.02] - 2023-07-27 +### Known Issues +- GCC 11.1.0 is unsupported due to compilation issues with select type. The issue is resolved in later GCC releases. +- When outputting sub-region diagnostics, the current diag_manager does not add "tileX" to the filename when using a cube sphere. This leads to trouble when trying to combine the files and regrid them (if the region is in two different tiles) +- GCC 10 and greater causing io issues when compiled using O2 optimization flags +- GNU compilers prior to the GCC 9.0 release are unsupported for this release due to lack of support for the findloc intrinsic function. This will result in an error saying 'findloc' has no IMPLICIT type and can be resolved by compiling with gcc version 9.0 or greater. + +### Added +- MPP/EXCHANGE: Adds association checks before pointer deallocations in mpp includes and xgrid + +### Changed +- LIBFMS: The libFMS.F90 file (module name `fms`) meant to provide global access has been updated to include 'fms' and it's module/subdirectory name as prefixes for all names. This will only affect external codes that are already using the global module (via `use fms`) and not individual modules. +- MIXED PRECISION: Updates the axis_utils2, horiz_interp, sat_vapor_pressure, and axis_utils subdirectories to support mixed precision real values. +- FMS2_IO: Added in mpp_scatter and mpp_gather performance changes from the 2023.01.01 patch. See below for more details. +- FMS2_IO: Improved error messages to give more debugging information +- FMS_MOD: Changed fms_init to include a system call to set the stack size to unlimited, removed previously added stack size fixes +- MONIN_OBUKHOV: Restructures the subroutines in `stable_mix` interface so that 1d calls the underlying implementation, and 2 and 3d call it on 1d slices of the data as opposed to passing in mismatched arrays. +- MPP: Updates from JEDI for ajoint version the mpp halo filling (mpp_do_update_ad.fh), adds checkpoint for forward buffer information. + +### Fixed +- MPP: mpp_broadcast causing an unintended error message due to checking the wrong pe value +- MPP: Added workaround for GCC 12 issues causing errors with string lengths in fms2_io +- FMS2_IO: Fixed support for 'packed' data when using NF_SHORT variables. Scale_factor and add_offset attributes will now be applied if present. +- DOCS: Improved doxygen comments for tranlon, updated deployment action for site +- TESTS: Workaround added for ICE coming from mpp_alltoall test with intel 2022.3, and fixes for any test scripts missing input.nml creation. Fixes for mpp/test_global_array failures. +- TIME_INTERP: Fixes crashes when calling with a non-existant field +- DIAG_MANAGER: Fixes a module dependency issue causing failures during parallel builds +- AXIS_UTILS2: Fixes an out of bounds memory index + +### Removed +- FMS_IO/MPP_IO: The two older io modules, fms_io_mod and mpp_io_mod, have been deprecated and will not be compiled by default. If you wish to compile these modules, you must use the -Duse_deprecated_io CPP flag or the --enable-deprecated-io configure option if building with autotools. + +### Tag Commit Hashes +- 2023.02-beta1 2be8aa452ad3e5f43e92c38a64f12d1ae6c43fb8 +- 2023.02-alpha3 8c73bd18dc1d580f2ee524c37cf903ff54d40501 +- 2023.02-alpha2 783019fdec89a8db2b26247c2f63d4782e1495c0 +- 2023.02-alpga1 419c66be31f82ebb13a91ea5e837c707eb54473b + + ## [2023.01.01] - 2023-06-06 ### Changed - FMS2_IO: Performance changes for domain_reads_2d and domain_reads_3d: diff --git a/CI.md b/CI.md index 225b25129b..89b4db256e 100644 --- a/CI.md +++ b/CI.md @@ -8,24 +8,30 @@ Required CI for pull requests are listed first. ## Pull Request CI and checks ### Build libFMS with autotools + Required GNU build test for all pull requests/pushes. Runs `make distcheck` after configuring via GNU autotools. +Runs on a container image with spack installed dependencies, on top a rocky linux base. + +Dockerfile for image is stored at .github/workflows/Dockerfile.gnu for more specific information on the CI environment. + Container environment: -gcc v7.3.0 -mpich v3.3a2 -netcdf v4.6.0 -netcdf-fortran v4.4.4 +gcc v12.3.0 +mpich v4.0.2 +netcdf v4.9.0 +netcdf-fortran v4.6.0 autoconf v2.69 +libyaml v0.2.5 -container hosted at [noaagfdl/ubuntu_libfms_gnu:latest](https://hub.docker.com/r/noaagfdl/ubuntu_libfms_gnu) - -`./configure` flags: -- `--enable-openmp` +`./configure` flags tested: - `--disable-openmp` - `--enable-mixed-mode` +- `--with-mpi=no` (disables unit testing) - `--disable-setting-flags` - `--with-yaml` +- `--enable-test-input=/home/unit_tests_input` + ### Build libfms with cmake Required GNU build test for all pull requests/pushes. diff --git a/CMakeLists.txt b/CMakeLists.txt index ab8f031872..a00ce9f847 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,7 @@ set(CMAKE_Fortran_FLAGS_DEBUG) # Define the CMake project project(FMS - VERSION 2023.01.0 + VERSION 2023.02.0 DESCRIPTION "GFDL FMS Library" HOMEPAGE_URL "https://www.gfdl.noaa.gov/fms" LANGUAGES C Fortran) @@ -196,6 +196,7 @@ list(APPEND fms_fortran_src_files # Collect FMS C source files list(APPEND fms_c_src_files affinity/affinity.c + fms/fms_stacksize.c mosaic/create_xgrid.c mosaic/gradient_c2l.c mosaic/interp.c diff --git a/astronomy/astronomy.F90 b/astronomy/astronomy.F90 index 7502aff260..0a7af2cc6e 100644 --- a/astronomy/astronomy.F90 +++ b/astronomy/astronomy.F90 @@ -831,7 +831,7 @@ function orbital_time(time) result(t) real(kind=r8_kind) :: t t = (time - autumnal_eq_ref)//period_time_type - t = twopi*(t - real(t, r8_kind)) + t = twopi*(t - real(floor(t), r8_kind)) if (time < autumnal_eq_ref) t = twopi - t end function orbital_time diff --git a/astronomy/include/astronomy.inc b/astronomy/include/astronomy.inc index 523413bb50..d26f97a7de 100644 --- a/astronomy/include/astronomy.inc +++ b/astronomy/include/astronomy.inc @@ -1463,7 +1463,7 @@ integer, parameter :: lkind = FMS_AST_KIND_ int = floor(norm_time) int = modulo(int,num_angles) int_1 = int+1 - x = norm_time - real((norm_time), FMS_AST_KIND_) + x = norm_time - real(floor(norm_time), FMS_AST_KIND_) ANGLE_ = (1.0_lkind - x) * real(orb_angle(int), FMS_AST_KIND_) & + x * real(orb_angle(int_1), FMS_AST_KIND_) ANGLE_ = modulo(ANGLE_, real(twopi, FMS_AST_KIND_)) diff --git a/configure.ac b/configure.ac index ad46025e48..d37a1a091d 100644 --- a/configure.ac +++ b/configure.ac @@ -25,7 +25,7 @@ AC_PREREQ([2.69]) # Initialize with name, version, and support email address. AC_INIT([GFDL FMS Library], - [2023.01.00-dev], + [2023.02.00-dev], [gfdl.climate.model.info@noaa.gov], [FMS], [https://www.github.com/NOAA-GFDL/FMS]) diff --git a/drifters/cloud_interpolator.F90 b/drifters/cloud_interpolator.F90 index f83f66274e..ef2ca004b2 100644 --- a/drifters/cloud_interpolator.F90 +++ b/drifters/cloud_interpolator.F90 @@ -25,6 +25,7 @@ !> @addtogroup cloud_interpolator_mod !> @{ MODULE cloud_interpolator_mod +#ifdef use_drifters implicit none private @@ -284,6 +285,7 @@ pure subroutine cld_ntrp_get_cell_values(nsizes, fnodes, indices, fvals, ier) end subroutine cld_ntrp_get_cell_values +#endif end MODULE cloud_interpolator_mod !=============================================================================== !> @} diff --git a/drifters/drifters.F90 b/drifters/drifters.F90 index eac1d6cbd8..2afd7068ac 100644 --- a/drifters/drifters.F90 +++ b/drifters/drifters.F90 @@ -60,6 +60,7 @@ !> @addtogroup drifters_mod !> @{ module drifters_mod +#ifdef use_drifters #ifdef _SERIAL @@ -947,7 +948,7 @@ subroutine drifters_reset_rk4(self, ermesg) endif end subroutine drifters_reset_rk4 - +#endif end module drifters_mod !> @} ! close documentation grouping diff --git a/drifters/drifters_comm.F90 b/drifters/drifters_comm.F90 index 5319e19934..e94e2a7f23 100644 --- a/drifters/drifters_comm.F90 +++ b/drifters/drifters_comm.F90 @@ -23,6 +23,7 @@ !> @brief Routines and types to update drifter positions across processor domains module drifters_comm_mod +#ifdef use_drifters #ifdef _SERIAL @@ -769,7 +770,7 @@ subroutine drifters_comm_gather(self, drfts, dinp, & end subroutine drifters_comm_gather - +#endif end module drifters_comm_mod !=============================================================================== diff --git a/drifters/drifters_core.F90 b/drifters/drifters_core.F90 index c25dd85e54..8d35d05cf4 100644 --- a/drifters/drifters_core.F90 +++ b/drifters/drifters_core.F90 @@ -21,6 +21,7 @@ !> @brief Handles the mechanics for adding and removing drifters module drifters_core_mod +#ifdef use_drifters use platform_mod implicit none private @@ -272,7 +273,7 @@ subroutine drifters_core_print(self1, ermesg1) end subroutine drifters_core_print - +#endif end module drifters_core_mod !############################################################################### !> @} diff --git a/drifters/drifters_input.F90 b/drifters/drifters_input.F90 index 0327f67053..157d12b215 100644 --- a/drifters/drifters_input.F90 +++ b/drifters/drifters_input.F90 @@ -23,6 +23,7 @@ !> @addtogroup drifters_input_mod !> @{ module drifters_input_mod +#ifdef use_drifters implicit none private @@ -444,7 +445,7 @@ subroutine drifters_input_save(self, filename, geolon, geolat, ermesg) & //nf_strerror(ier) end subroutine drifters_input_save - +#endif end module drifters_input_mod !> @} ! close documentation grouping diff --git a/drifters/drifters_io.F90 b/drifters/drifters_io.F90 index 3592da9603..e9754f4487 100644 --- a/drifters/drifters_io.F90 +++ b/drifters/drifters_io.F90 @@ -23,6 +23,7 @@ !> @addtogroup drifters_io_mod !> @{ module drifters_io_mod +#ifdef use_drifters use netcdf use netcdf_nf_data @@ -307,7 +308,7 @@ subroutine drifters_io_write(self, time, np, nd, nf, ids, positions, fields, erm self%it_id = self%it_id + np end subroutine drifters_io_write - +#endif end module drifters_io_mod !> @} ! close documentation grouping diff --git a/drifters/include/cloud_interpolator.inc b/drifters/include/cloud_interpolator.inc deleted file mode 100644 index f83f66274e..0000000000 --- a/drifters/include/cloud_interpolator.inc +++ /dev/null @@ -1,290 +0,0 @@ -!*********************************************************************** -!* GNU Lesser General Public License -!* -!* This file is part of the GFDL Flexible Modeling System (FMS). -!* -!* FMS is free software: you can redistribute it and/or modify it under -!* the terms of the GNU Lesser General Public License as published by -!* the Free Software Foundation, either version 3 of the License, or (at -!* your option) any later version. -!* -!* FMS is distributed in the hope that it will be useful, but WITHOUT -!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -!* for more details. -!* -!* You should have received a copy of the GNU Lesser General Public -!* License along with FMS. If not, see . -!*********************************************************************** -#define _FLATTEN(A) reshape((A), (/size((A))/) ) - -!> @defgroup cloud_interpolator_mod cloud_interpolator_mod -!> @ingroup drifters -!! @brief Cloud interpolation routines for use in @ref drifters_mod - -!> @addtogroup cloud_interpolator_mod -!> @{ -MODULE cloud_interpolator_mod - implicit none - private - - public :: cld_ntrp_linear_cell_interp, cld_ntrp_locate_cell, cld_ntrp_get_cell_values - public :: cld_ntrp_expand_index, cld_ntrp_contract_indices - -! Include variable "version" to be written to log file. -#include -real, parameter :: tol = 10.0*epsilon(1.) - -CONTAINS - -!............................................................................... -!> Get expanded list of indices from contracted index -!> @param Ic contracted index -!> @param[out] ie(:) expanded list of indices -!> @param[out] ier error flag, non zero if operation unsuccessful -pure subroutine cld_ntrp_expand_index(Ic, ie, ier) - integer, intent(in) :: Ic - integer, intent(out) :: ie(:) - integer, intent(out) :: ier - - integer j, nd - - ier = 0 - nd = size(ie) ! dimension - - if(Ic >= 2**nd) then - ie = -1 - ier = 1 ! error - return - endif - - do j = 1, nd - ie(j) = mod(Ic/2**(j-1), 2) - end do - - end subroutine cld_ntrp_expand_index - -!............................................................................... -!> Contract list of indices to an single integer -!> @param ie(:) expanded list of indices -!> @param[out] Ic contracted index -!> @param[out] ier error flag, non zero if operation unsuccessful -pure subroutine cld_ntrp_contract_indices(ie, Ic, ier) - integer, intent(in) :: ie(:) - integer, intent(out) :: Ic - integer, intent(out) :: ier - - integer j, nd - - ier = 0 - nd = size(ie) ! dimension - - Ic = ie(nd) - do j = nd-1, 1, -1 - Ic = Ic * 2 - Ic = Ic + ie(j) - end do - - if(Ic >= 2**nd) ier = 1 - - end subroutine cld_ntrp_contract_indices - - -!............................................................................... -!............................................................................... -!> Cloud interpolation for linear cells -!> @param fvals values at the cell nodes -!> @param ts normalized [0,1]^nd cell coordinates -!> @param[out] interpolated value -!> @param[out] error flag, non zero if unsucessful -pure subroutine cld_ntrp_linear_cell_interp(fvals, ts, f, ier) - real, intent(in) :: fvals(0:) - real, intent(in) :: ts(:) - real, intent(out):: f - integer, intent(out) :: ier - - integer j, nd, Ic, iflag - integer ie(size(fvals)) - real basis - - ier = 0 - f = 0. - nd = size(ts) - if(size(fvals) /= 2**nd) then - ier = 1 - return - endif - - do Ic = 0, 2**nd - 1 - basis = 1. - call cld_ntrp_expand_index(Ic, ie, iflag) - do j = 1, nd - basis = basis * ( (1.0-real(ie(j)))*(1.0-ts(j)) + real(ie(j))*ts(j) ) - end do - f = f + fvals(Ic)*basis - end do - - end subroutine cld_ntrp_linear_cell_interp - -!............................................................................... -!............................................................................... -pure subroutine cld_ntrp_locate_cell(axis, x, index, ier) - real, intent(in) :: axis(:) !< axis - real, intent(in) :: x !< abscissae - integer, intent(out) :: index !< lower-left corner index - integer, intent(out) :: ier !< error flag (0=ok) - - logical down - integer n, index1, is - real axis_1, axis_n, axis_min, axis_max - ier = 0 - index = -1 - down = .FALSE. - n = size(axis) - if(n < 2) then - ier = 3 - return - endif - axis_1 = axis(1) - axis_n = axis(n) - axis_min = axis_1 - axis_max = axis_n - if(axis_1 > axis_n) then - down = .TRUE. - axis_min = axis_n - axis_max = axis_1 - endif - - if(x < axis_min-tol) then - ier = 1 - return - endif - if(x > axis_max+tol) then - ier = 2 - return - endif - - index = floor(real(n-1)*(x - axis_1)/(axis_n-axis_1)) + 1 - index = min(n-1, index) - index1 = index+1 - - if(.NOT. down) then - if(axis(index) <= x+tol) then - if(x <= axis(index1)+tol) then - ! axis is uniform, or nearly so. Done! - return - else - ! increase index - is = index+1 - do index = is, n-1 - index1 = index+1 - if(axis(index1) >= x-tol) return - enddo - endif - else - ! decrease index - is = index - 1 - do index = is, 1, -1 - if(axis(index) <= x+tol) return - enddo - endif - else - ! axis is pointing down - if(axis(index) >= x-tol) then - if(x >= axis(index1)-tol) then - ! axis is uniform, or nearly so. Done! - return - else - ! increase index - is = index + 1 - do index = is, n-1 - index1 = index+1 - if(axis(index1) <= x+tol) return - enddo - endif - else - ! decrease index - is = index - 1 - do index = is, 1, -1 - if(axis(index) >= x-tol) return - enddo - endif - endif - - end subroutine cld_ntrp_locate_cell - -!............................................................................... -!............................................................................... -pure subroutine cld_ntrp_get_flat_index(nsizes, indices, flat_index, ier) - integer, intent(in) :: nsizes(:) !< size of array along each axis - integer, intent(in) :: indices(:) !< cell indices - integer, intent(out) :: flat_index !< index into flattened array - integer, intent(out) :: ier !< error flag (0=ok) - - integer nd, id - - ier = 0 - flat_index = -1 - nd = size(nsizes) - if(nd /= size(indices)) then - ! size mismatch - ier = 1 - return - endif - - flat_index = indices(nd)-1 - do id = nd-1, 1, -1 - flat_index = flat_index*nsizes(id) + indices(id)-1 - enddo - flat_index = flat_index + 1 - - end subroutine cld_ntrp_get_flat_index - -!............................................................................... -!............................................................................... -pure subroutine cld_ntrp_get_cell_values(nsizes, fnodes, indices, fvals, ier) - integer, intent(in) :: nsizes(:) !< size of fnodes along each axis - real, intent(in) :: fnodes(:) !< flattened array of node values - integer, intent(in) :: indices(:) !< cell indices - real, intent(out) :: fvals(0:) !< returned array values in the cell - integer, intent(out) :: ier !< error flag (0=ok) - - integer id, nt, nd, flat_index, Ic, iflag - integer, dimension(size(nsizes)) :: cell_indices, node_indices - ier = 0 - fvals = 0. - - nd = size(nsizes) - if(nd /= size(indices)) then - ! size mismatch - ier = 1 - return - endif - if(2**nd > size(fvals)) then - ! not enough elements to hold result - ier = 2 - return - endif - nt = 1 - do id = 1, nd - nt = nt * nsizes(id) - enddo - if(nt /= size(fnodes)) then - ! not enough node values - ier = 3 - return - endif - - do Ic = 0, 2**nd-1 - call cld_ntrp_expand_index(Ic, cell_indices, iflag) - node_indices = indices + cell_indices - call cld_ntrp_get_flat_index(nsizes, node_indices, flat_index, iflag) - fvals(Ic) = fnodes(flat_index) - enddo - - end subroutine cld_ntrp_get_cell_values - -end MODULE cloud_interpolator_mod -!=============================================================================== -!> @} -! close documentation grouping diff --git a/drifters/include/drifters.inc b/drifters/include/drifters.inc deleted file mode 100644 index eac1d6cbd8..0000000000 --- a/drifters/include/drifters.inc +++ /dev/null @@ -1,953 +0,0 @@ -!*********************************************************************** -!* GNU Lesser General Public License -!* -!* This file is part of the GFDL Flexible Modeling System (FMS). -!* -!* FMS is free software: you can redistribute it and/or modify it under -!* the terms of the GNU Lesser General Public License as published by -!* the Free Software Foundation, either version 3 of the License, or (at -!* your option) any later version. -!* -!* FMS is distributed in the hope that it will be useful, but WITHOUT -!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -!* for more details. -!* -!* You should have received a copy of the GNU Lesser General Public -!* License along with FMS. If not, see . -!*********************************************************************** - -#include "fms_switches.h" -#define _FLATTEN(A) reshape((A), (/size((A))/) ) - -!> @defgroup drifters_mod drifters_mod -!> @ingroup drifters -!! @brief Drifters_modis a module designed to advect a set of particles, in parallel or -!! sequentially, given an prescribed velocity field. -!! @author Alexander Pletzer -!! -!> Drifters are idealized point particles with positions that evolve in time according -!! to a prescribed velocity field, starting from some initial conditions. Drifters have -!! no mass, no energy, no size, and no friction and therefore have no impact on the -!! dynamics of the underlying system. The only feature that distinguishes a drifter -!! from another is its trajectory. This makes drifters ideal for tracking pollution -!! clouds and probing fields (e.g. temperature, salinity) along ocean currents, to name -!! a few applications. -!! Drifters can mimic real experiments such as the Argo floats -!! http://www.metoffice.com/research/ocean/argo/ukfloats.html. -!! -!! When run in parallel, on a 2d decomposed domain, drifters_mod will handle all the -!! bookkeeping and communication transparently for the user. This involves adding/removing -!! drifters as they enter/leave a processor element (PE) domain. Note that the number of drifters -!! can vary greatly both between PE domains and within a PE domain in the course of a simulation; the drifters' -!! module will also manage dynamically the memory for the user. -!! -!! There are a number of basic assumptions which could make the drifters' module -!! ill-suited for some tasks. First and foremost, it is assumed that the motion of -!! drifters is not erratic but follows deterministic trajectories. Furthermore, -!! drifters should not cross both compute and data domain boundaries within less -!! than a time step. This limitation is imposed by the Runge-Kutta integration -!! scheme, which must be able to complete, within a time step, a trajectory -!! calculation that starts inside the compute domain and ends inside the data domain. Therefore, the drifters, -!! as they are presently modelled, are unlikely to work for very fast objects. -!! This constraint also puts a upper limit to the domain decomposition, although -!! it can often be remedied by increasing the number of ghost nodes. -!! -!! Another fundamental assumption is that the (e.g. velocity) fields are structured, -!! on a per PE domain basis. There is no support for locally nested or unstrucured -!! meshes. Meshes need not be smooth and continuous across PE domains, however. - -!> @addtogroup drifters_mod -!> @{ -module drifters_mod - -#ifdef _SERIAL - -! serial code -#define _MPP_PE 0 -#define _MPP_ROOT 0 -#define _MPP_NPES 1 -#define _TYPE_DOMAIN2D integer - -#else - -! parallel code - use mpp_mod , only : mpp_pe, mpp_npes - use mpp_domains_mod, only : domain2d -#define _MPP_PE mpp_pe() -#define _MPP_ROOT mpp_root_pe() -#define _MPP_NPES mpp_npes() -#define _TYPE_DOMAIN2D type(domain2d) - -#endif - - use drifters_core_mod, only: drifters_core_type, drifters_core_new, drifters_core_del, assignment(=) - - use drifters_input_mod, only: drifters_input_type, drifters_input_new, drifters_input_del, assignment(=) - - use drifters_io_mod, only: drifters_io_type, drifters_io_new, drifters_io_del, drifters_io_set_time_units, & - drifters_io_set_position_names, drifters_io_set_position_units, & - drifters_io_set_field_names, drifters_io_set_field_units, drifters_io_write - - use drifters_comm_mod, only: drifters_comm_type,drifters_comm_new,drifters_comm_del, & - drifters_comm_set_pe_neighbors, drifters_comm_set_domain, & - drifters_comm_gather, drifters_comm_update - - use cloud_interpolator_mod, only: cld_ntrp_linear_cell_interp, cld_ntrp_locate_cell, cld_ntrp_get_cell_values - implicit none - private - - public :: drifters_type, assignment(=), drifters_push, drifters_compute_k, drifters_set_field - public :: drifters_new, drifters_del, drifters_set_domain, drifters_set_pe_neighbors - public :: drifters_set_v_axes, drifters_set_domain_bounds, drifters_positions2lonlat - public :: drifters_print_checksums, drifters_save, drifters_write_restart, drifters_distribute - - integer, parameter, private :: MAX_STR_LEN = 128 -! Include variable "version" to be written to log file. -#include - !> @} - - !> @brief Holds all data needed for drifters communication, io, and input. - !> @ingroup drifters_mod - type drifters_type - ! Be sure to update drifters_new, drifters_del and drifters_copy_new - ! when adding members - type(drifters_core_type) :: core - type(drifters_input_type) :: input - type(drifters_io_type) :: io - type(drifters_comm_type) :: comm - real :: dt !< total dt, over a complete step - real :: time - ! fields - real, allocatable :: fields(:,:) - ! velocity field axes - real, allocatable :: xu(:) !< velocity field axes - real, allocatable :: yu(:) !< velocity field axes - real, allocatable :: zu(:) !< velocity field axes - real, allocatable :: xv(:) !< velocity field axes - real, allocatable :: yv(:) !< velocity field axes - real, allocatable :: zv(:) !< velocity field axes - real, allocatable :: xw(:) !< velocity field axes - real, allocatable :: yw(:) !< velocity field axes - real, allocatable :: zw(:) !< velocity field axes - ! Runge Kutta coefficients holding intermediate results (positions) - real, allocatable :: temp_pos(:,:) !< Runge Kutta coefficients holding - !! intermediate results (positions) - real, allocatable :: rk4_k1(:,:) !< Runge Kutta coefficients holding - !! intermediate results (positions) - real, allocatable :: rk4_k2(:,:) !< Runge Kutta coefficients holding - !! intermediate results (positions) - real, allocatable :: rk4_k3(:,:) !< Runge Kutta coefficients holding - !! intermediate results (positions) - real, allocatable :: rk4_k4(:,:) !< Runge Kutta coefficients holding - !! intermediate results (positions) - ! store filenames for convenience - character(len=MAX_STR_LEN) :: input_file !< store filenames for convenience - character(len=MAX_STR_LEN) :: output_file !< store filenames for convenience - ! Runge Kutta stuff - integer :: rk4_step !< Runge Kutta stuff - logical :: rk4_completed !< Runge Kutta stuff - integer :: nx, ny - logical, allocatable :: remove(:) - end type drifters_type - - !> @brief Assignment override for @ref drifters_type - !> @ingroup drifters_mod - interface assignment(=) - module procedure drifters_copy_new - end interface - - !> @brief "Push" a given drifter at a given velocity for either 2D or 3D data - !> @ingroup drifters_mod - interface drifters_push - module procedure drifters_push_2 - module procedure drifters_push_3 - end interface - - !> @ingroup drifters_mod - interface drifters_compute_k - module procedure drifters_computek2d - module procedure drifters_computek3d - end interface - - !> @brief Set the value of a given drifter field - !> @ingroup drifters_mod - interface drifters_set_field - module procedure drifters_set_field_2d - module procedure drifters_set_field_3d - end interface - -!> @addtogroup drifters_mod -!> @{ - -contains - - !> @brief Will read positions stored in the netCDF file input_file. - !! - !> The trajectories will be saved in files output_file.PE, - !! one file per PE domain. - subroutine drifters_new(self, input_file, output_file, ermesg) - - type(drifters_type) :: self !< Opaque data structure. - character(len=*), intent(in) :: input_file !< NetCDF input file name containing initial positions. - character(len=*), intent(in) :: output_file !< NetCDF output file. Will contain trajectory - !! positions and interpolated fields. - character(len=*), intent(out) :: ermesg !< Error message (if any). - - integer nd, nf, npdim, i - character(len=6) :: pe_str - - ermesg = '' - - self%input_file = input_file - self%output_file = output_file - - call drifters_input_new(self%input, input_file, ermesg) - if(ermesg/='') return - - ! number of dimensions - nd = size(self%input%velocity_names) - ! estimate for the max number of particles (will resize if exceeded) - npdim = int(1.3*size(self%input%positions, 2)) - call drifters_core_new(self%core, nd=nd, npdim=npdim, ermesg=ermesg) - if(ermesg/='') return - - ! number of fields - nf = size(self%input%field_names) - - ! one output file per PE - pe_str = ' ' - write(pe_str, '(i6)') _MPP_PE - pe_str = adjustr(pe_str) - do i = 1, 5 - if(pe_str(i:i)==' ') pe_str(i:i)='0' - enddo - call drifters_io_new(self%io, output_file//'.'//pe_str, nd, nf, ermesg) - if(ermesg/='') return - - call drifters_comm_new(self%comm) - if(ermesg/='') return - - ! Set meta data - call drifters_io_set_time_units(self%io, name=self%input%time_units, & - & ermesg=ermesg) - - call drifters_io_set_position_names(self%io, names=self%input%position_names, & - & ermesg=ermesg) - if(ermesg/='') return - call drifters_io_set_position_units(self%io, names=self%input%position_units, & - & ermesg=ermesg) - if(ermesg/='') return - - call drifters_io_set_field_names(self%io, names=self%input%field_names, & - & ermesg=ermesg) - if(ermesg/='') return - call drifters_io_set_field_units(self%io, names=self%input%field_units, & - & ermesg=ermesg) - if(ermesg/='') return - - self%dt = -1 - self%time = -1 - self%rk4_step = 0 - self%nx = 0 - self%ny = 0 - self%rk4_completed = .FALSE. - - allocate(self%rk4_k1(self%core%nd, self%core%npdim)) - self%rk4_k1 = -huge(1.) - allocate(self%rk4_k2(self%core%nd, self%core%npdim)) - self%rk4_k2 = -huge(1.) - allocate(self%rk4_k3(self%core%nd, self%core%npdim)) - self%rk4_k3 = -huge(1.) - allocate(self%rk4_k4(self%core%nd, self%core%npdim)) - self%rk4_k4 = -huge(1.) - allocate(self%remove(self%core%npdim)) - self%remove = .FALSE. - allocate(self%temp_pos(nd, self%core%npdim)) - self%temp_pos = -huge(1.) - - allocate(self%fields(nf, self%core%npdim)) - self%fields = -huge(1.) - - end subroutine drifters_new - - !============================================================================ - - !> @brief Destructor, call this to reclaim memory from data used for drifters. - subroutine drifters_del(self, ermesg) - type(drifters_type) :: self !< Opaque data structure. - character(len=*), intent(out) :: ermesg !< Error message (if any). - - integer flag - ermesg = '' - deallocate(self%fields, stat=flag) - deallocate(self%xu, stat=flag) - deallocate(self%yu, stat=flag) - deallocate(self%zu, stat=flag) - deallocate(self%xv, stat=flag) - deallocate(self%yv, stat=flag) - deallocate(self%zv, stat=flag) - deallocate(self%xw, stat=flag) - deallocate(self%yw, stat=flag) - deallocate(self%zw, stat=flag) - deallocate(self%temp_pos, stat=flag) - deallocate(self%rk4_k1, stat=flag) - deallocate(self%rk4_k2, stat=flag) - deallocate(self%rk4_k3, stat=flag) - deallocate(self%rk4_k4, stat=flag) - deallocate(self%remove, stat=flag) - - call drifters_core_del(self%core, ermesg) - if(ermesg/='') return - call drifters_input_del(self%input, ermesg) - if(ermesg/='') return - call drifters_io_del(self%io, ermesg) - if(ermesg/='') return - call drifters_comm_del(self%comm) - if(ermesg/='') return - - end subroutine drifters_del - - !============================================================================ - !> @brief Copy a drifter state into a new state. Note: this will not open new files; this will - !! copy all members into a new container. - subroutine drifters_copy_new(new_instance, old_instance) - - type(drifters_type), intent(in) :: old_instance !< Old data structure. - type(drifters_type), intent(inout) :: new_instance !< New data structure. - - character(len=MAX_STR_LEN) :: ermesg - - ermesg = '' - - ! make sure new_instance is empty - call drifters_del(new_instance, ermesg) - if(ermesg/='') return - - new_instance%core = old_instance%core - new_instance%input = old_instance%input - new_instance%io = old_instance%io - new_instance%comm = old_instance%comm - - new_instance%dt = old_instance%dt - new_instance%time = old_instance%time - - allocate(new_instance%fields( size(old_instance%fields, 1), & - & size(old_instance%fields, 2) )) - new_instance%fields = old_instance%fields - - allocate(new_instance%xu( size(old_instance%xu) )) - allocate(new_instance%yu( size(old_instance%yu) )) - allocate(new_instance%zu( size(old_instance%zu) )) - new_instance%xu = old_instance%xu - new_instance%yu = old_instance%yu - new_instance%zu = old_instance%zu - allocate(new_instance%xv( size(old_instance%xv) )) - allocate(new_instance%yv( size(old_instance%yv) )) - allocate(new_instance%zv( size(old_instance%zv) )) - new_instance%xv = old_instance%xv - new_instance%yv = old_instance%yv - new_instance%zv = old_instance%zv - allocate(new_instance%xw( size(old_instance%xw) )) - allocate(new_instance%yw( size(old_instance%yw) )) - allocate(new_instance%zw( size(old_instance%zw) )) - new_instance%xw = old_instance%xw - new_instance%yw = old_instance%yw - new_instance%zw = old_instance%zw - - allocate(new_instance%temp_pos( size(old_instance%temp_pos,1), & - & size(old_instance%temp_pos,2) )) - new_instance%temp_pos = old_instance%temp_pos - allocate(new_instance%rk4_k1( size(old_instance%rk4_k1,1), & - & size(old_instance%rk4_k1,2) )) - allocate(new_instance%rk4_k2( size(old_instance%rk4_k2,1), & - & size(old_instance%rk4_k2,2) )) - allocate(new_instance%rk4_k3( size(old_instance%rk4_k3,1), & - & size(old_instance%rk4_k3,2) )) - allocate(new_instance%rk4_k4( size(old_instance%rk4_k4,1), & - & size(old_instance%rk4_k4,2) )) - new_instance%rk4_k1 = old_instance%rk4_k1 - new_instance%rk4_k2 = old_instance%rk4_k2 - new_instance%rk4_k3 = old_instance%rk4_k3 - new_instance%rk4_k4 = old_instance%rk4_k4 - - new_instance%rk4_step = old_instance%rk4_step - new_instance%rk4_completed = old_instance%rk4_completed - new_instance%nx = old_instance%nx - new_instance%ny = old_instance%ny - - allocate(new_instance%remove(size(old_instance%remove))) - new_instance%remove = old_instance%remove - - - end subroutine drifters_copy_new - - !============================================================================ - !> @brief Set the compute, data, and global domain boundaries. - !! @details The data domain extends beyond the compute domain and is shared between - !! two or more PE domains. A particle crossing the compute domain boundary - !! will trigger a communication with one or more neighboring domains. A particle - !! leaving the data domain will be removed from the list of particles. - !! - !!
Example usage: - !! @code{.F90} - !! call drifters_set_domain(self, & - !! & xmin_comp, xmax_comp, ymin_comp, ymax_comp, & - !! & xmin_data, xmax_data, ymin_data, ymax_data, & - !! & xmin_glob, xmax_glob, ymin_glob, ymax_glob, & - !! & ermesg) - !! @endcode - subroutine drifters_set_domain(self, & - & xmin_comp, xmax_comp, ymin_comp, ymax_comp, & - & xmin_data, xmax_data, ymin_data, ymax_data, & - & xmin_glob, xmax_glob, ymin_glob, ymax_glob, & - & ermesg) - type(drifters_type) :: self !< Opaque data structure. - ! compute domain boundaries - real, optional, intent(in) :: xmin_comp !< Min of longitude-like axis on compute domain. - real, optional, intent(in) :: xmax_comp !< Max of longitude-like axis on compute domain. - real, optional, intent(in) :: ymin_comp !< Min of latitude-like axis on compute domain. - real, optional, intent(in) :: ymax_comp !< Max of latitude-like axis on compute domain. - ! data domain boundaries - real, optional, intent(in) :: xmin_data !< Min of longitude-like axis on data domain. - real, optional, intent(in) :: xmax_data !< Max of longitude-like axis on data domain. - real, optional, intent(in) :: ymin_data !< Min of latitude-like axis on data domain. - real, optional, intent(in) :: ymax_data !< Max of latitude-like axis on data domain. - ! global boundaries (only specify those if domain is periodic) - real, optional, intent(in) :: xmin_glob !< Min of longitude-like axis on global domain. - real, optional, intent(in) :: xmax_glob !< Max of longitude-like axis on global domain. - real, optional, intent(in) :: ymin_glob !< Min of latitude-like axis on global domain. - real, optional, intent(in) :: ymax_glob !< Max of latitude-like axis on global domain. - character(len=*), intent(out) :: ermesg !< Error message (if any). - - ermesg = '' - if(present(xmin_comp)) self%comm%xcmin = xmin_comp - if(present(xmax_comp)) self%comm%xcmax = xmax_comp - if(present(ymin_comp)) self%comm%ycmin = ymin_comp - if(present(ymax_comp)) self%comm%ycmax = ymax_comp - - if(present(xmin_data)) self%comm%xdmin = xmin_data - if(present(xmax_data)) self%comm%xdmax = xmax_data - if(present(ymin_data)) self%comm%ydmin = ymin_data - if(present(ymax_data)) self%comm%ydmax = ymax_data - - if(present(xmin_glob)) self%comm%xgmin = xmin_glob - if(present(xmax_glob)) self%comm%xgmax = xmax_glob - if(present(ymin_glob)) self%comm%ygmin = ymin_glob - if(present(ymax_glob)) self%comm%ygmax = ymax_glob - - ! Note: the presence of both xgmin/xgmax will automatically set the - ! periodicity flag - if(present(xmin_glob) .and. present(xmax_glob)) self%comm%xperiodic = .TRUE. - if(present(ymin_glob) .and. present(ymax_glob)) self%comm%yperiodic = .TRUE. - - end subroutine drifters_set_domain - - !============================================================================ - !> @brief Given an MPP based deomposition, set the PE numbers that are adjacent to this - !! processor. - !! - !> This will allow several PEs to track the trajectories of particles in the buffer regions. - subroutine drifters_set_pe_neighbors(self, domain, ermesg) - - type(drifters_type) :: self !< Opaque data structure. - _TYPE_DOMAIN2D :: domain !< MPP domain. - character(len=*), intent(out) :: ermesg !< Error message (if any). - - ermesg = '' - - call drifters_comm_set_pe_neighbors(self%comm, domain) - - end subroutine drifters_set_pe_neighbors - - !============================================================================ -#define _DIMS 2 -#define drifters_push_XXX drifters_push_2 -#include "drifters_push.fh" -#undef _DIMS -#undef drifters_push_XXX - - !============================================================================ -#define _DIMS 3 -#define drifters_push_XXX drifters_push_3 -#include "drifters_push.fh" -#undef _DIMS -#undef drifters_push_XXX - - !============================================================================ - subroutine drifters_modulo(self, positions, ermesg) - type(drifters_type) :: self - real, intent(inout) :: positions(:,:) - character(len=*), intent(out) :: ermesg - - integer ip, np - real x, y - - ermesg = '' - np = self%core%np - - if(self%comm%xperiodic) then - do ip = 1, np - x = positions(1, ip) - positions(1, ip) = self%comm%xgmin + & - & modulo(x - self%comm%xgmin, self%comm%xgmax-self%comm%xgmin) - enddo - endif - - if(self%comm%yperiodic) then - do ip = 1, np - y = positions(2, ip) - positions(2, ip) = self%comm%ygmin + & - & modulo(y - self%comm%ygmin, self%comm%ygmax-self%comm%ygmin) - enddo - endif - - end subroutine drifters_modulo - - !============================================================================ -#define _DIMS 2 -#define drifters_set_field_XXX drifters_set_field_2d -#include "drifters_set_field.fh" -#undef _DIMS -#undef drifters_set_field_XXX - - !============================================================================ -#define _DIMS 3 -#define drifters_set_field_XXX drifters_set_field_3d -#include "drifters_set_field.fh" -#undef _DIMS -#undef drifters_set_field_XXX - !============================================================================ - !> @brief Append new positions to NetCDF file. - !! - !> Use this method to append the new trajectory positions and the interpolated - !! probe fields to a netCDF file. - subroutine drifters_save(self, ermesg) - type(drifters_type) :: self !< Opaque daata structure. - character(len=*), intent(out) :: ermesg !< Error message (if any). - - integer nf, np - - ermesg = '' - nf = size(self%input%field_names) - np = self%core%np - - ! save to disk - call drifters_io_write(self%io, self%time, np, self%core%nd, nf, & - & self%core%ids, self%core%positions, & - & fields=self%fields(:,1:np), ermesg=ermesg) - - end subroutine drifters_save - !============================================================================ - - !> @brief Distribute particles across PEs. - !! - !> Use this method after setting the domain boundaries - !! (drifters_set_domain) to spread the particles across PE domains. - subroutine drifters_distribute(self, ermesg) - type(drifters_type) :: self !< Opaque handle. - character(len=*), intent(out) :: ermesg !< Error message (if any). - - real x, y - integer i, nptot, nd - - ermesg = '' - nd = self%core%nd - if(nd < 2) then - ermesg = 'drifters_distribute: dimension must be >=2' - return - endif - - nptot = size(self%input%positions, 2) - do i = 1, nptot - x = self%input%positions(1,i) - y = self%input%positions(2,i) - if(x >= self%comm%xdmin .and. x <= self%comm%xdmax .and. & - & y >= self%comm%ydmin .and. y <= self%comm%ydmax) then - - self%core%np = self%core%np + 1 - self%core%positions(1:nd, self%core%np) = self%input%positions(1:nd, i) - self%core%ids(self%core%np) = i - - endif - enddo - - end subroutine drifters_distribute - - !============================================================================ - !> @brief Write restart file for drifters. - !! - !> Gather all the particle positions distributed - !! across PE domains on root PE and save the data in netCDF file. - subroutine drifters_write_restart(self, filename, & - & x1, y1, geolon1, & - & x2, y2, geolat2, & - & root, mycomm, ermesg) - ! gather all positions and ids and save the result in - ! self%input data structure on PE "root", then write restart file - - type(drifters_type) :: self !< Opaque data structure. - character(len=*), intent(in) :: filename !< Restart file name. - - ! if these optional arguments are passed, the positions will - ! mapped to lon/lat degrees and saved in the file. - real, intent(in), optional :: x1(:) !< Pseudo-longitude axis supporting longitudes. - real, intent(in), optional :: y1(:) !< Pseudo-latitude axis supporting longitudes. - real, intent(in), optional :: geolon1(:,:) !< Longitude array (x1, y1). - real, intent(in), optional :: x2(:) !< Pseudo-longitude axis supporting latitudes. - real, intent(in), optional :: y2(:) !< Pseudo-latitude axis supporting latitudes. - real, intent(in), optional :: geolat2(:,:) !< Latitudes array (x2, y2) - - - integer, intent(in), optional :: root !< root pe - integer, intent(in), optional :: mycomm !< MPI communicator - character(len=*), intent(out) :: ermesg !< Error message (if any). - - integer :: np - logical :: do_save_lonlat - real, allocatable :: lons(:), lats(:) - - ermesg = '' - - np = self%core%np - - allocate(lons(np), lats(np)) - lons = -huge(1.) - lats = -huge(1.) - - ! get lon/lat if asking for - if(present(x1) .and. present(y1) .and. present(geolon1) .and. & - & present(x2) .and. present(y2) .and. present(geolat2)) then - do_save_lonlat = .TRUE. - else - do_save_lonlat = .FALSE. - endif - - if(do_save_lonlat) then - - ! Interpolate positions onto geo longitudes/latitudes - call drifters_positions2lonlat(self, & - & positions=self%core%positions(:,1:np), & - & x1=x1, y1=y1, geolon1=geolon1, & - & x2=x2, y2=y2, geolat2=geolat2, & - & lons=lons, lats=lats, ermesg=ermesg) - if(ermesg/='') return ! problems, bail off - - endif - - call drifters_comm_gather(self%comm, self%core, self%input, & - & lons, lats, do_save_lonlat, & - & filename, & - & root, mycomm) - - end subroutine drifters_write_restart - - !============================================================================ -#define _DIMS 2 -#define drifters_compute_k_XXX drifters_computek2d -#include "drifters_compute_k.fh" -#undef _DIMS -#undef drifters_compute_k_XXX - - !============================================================================ -#define _DIMS 3 -#define drifters_compute_k_XXX drifters_computek3d -#include "drifters_compute_k.fh" -#undef _DIMS -#undef drifters_compute_k_XXX - - - !============================================================================ - !> @brief Set velocity field axes. - !! @details Velocity axis components may be located on different grids or cell faces. For instance, zonal (u) - !! and meridional (v) velcity components are staggered by half a cell size in Arakawa's C and D grids. - !! This call will set individual axes for each components do as to allow interpolation of the velocity - !! field on arbitrary positions. - subroutine drifters_set_v_axes(self, component, x, y, z, ermesg) - type(drifters_type) :: self !< Opaque data structure. - character(len=*), intent(in) :: component !< Velocity component: either 'u', 'v', or 'w'. - real, intent(in) :: x(:) !< X-axis. - real, intent(in) :: y(:) !< Y-axis. - real, intent(in) :: z(:) !< Z-axis. - character(len=*), intent(out) :: ermesg !< Error message (if any). - - integer ier, nx, ny, nz - - ermesg = '' - nx = size(x) - ny = size(y) - nz = size(z) - select case (component(1:1)) - case ('u', 'U') - if(nx > 0) then - deallocate(self%xu, stat=ier) - allocate(self%xu(nx)) - self%xu = x - self%nx = max(self%nx, size(x)) - endif - if(ny > 0) then - deallocate(self%yu, stat=ier) - allocate(self%yu(ny)) - self%yu = y - self%ny = max(self%ny, size(y)) - endif - if(nz > 0) then - deallocate(self%zu, stat=ier) - allocate(self%zu(nz)) - self%zu = z - endif - case ('v', 'V') - if(nx > 0) then - deallocate(self%xv, stat=ier) - allocate(self%xv(nx)) - self%xv = x - self%nx = max(self%nx, size(x)) - endif - if(ny > 0) then - deallocate(self%yv, stat=ier) - allocate(self%yv(ny)) - self%yv = y - self%ny = max(self%ny, size(y)) - endif - if(nz > 0) then - deallocate(self%zv, stat=ier) - allocate(self%zv(nz)) - self%zv = z - endif - case ('w', 'W') - if(nx > 0) then - deallocate(self%xw, stat=ier) - allocate(self%xw(nx)) - self%xw = x - self%nx = max(self%nx, size(x)) - endif - if(ny > 0) then - deallocate(self%yw, stat=ier) - allocate(self%yw(ny)) - self%yw = y - self%ny = max(self%ny, size(y)) - endif - if(nz > 0) then - deallocate(self%zw, stat=ier) - allocate(self%zw(nz)) - self%zw = z - endif - case default - ermesg = 'drifters_set_v_axes: ERROR component must be "u", "v" or "w"' - end select - end subroutine drifters_set_v_axes - - !============================================================================ - !> @brief Set boundaries of "data" and "compute" domains - !! @details Each particle will be tracked sol long is it is located in the data domain. - subroutine drifters_set_domain_bounds(self, domain, backoff_x, backoff_y, ermesg) - type(drifters_type) :: self !< Opaque data structure. - _TYPE_DOMAIN2D :: domain !< Instance of Domain2D (see mpp_domain) - integer, intent(in) :: backoff_x !< particles leaves domain when crossing ied-backoff_x - integer, intent(in) :: backoff_y !< particles leaves domain when crossing jed-backoff_y - character(len=*), intent(out) :: ermesg !< Error message (if any). - - ermesg = '' - - if(.not.allocated(self%xu) .or. .not.allocated(self%yu)) then - ermesg = 'drifters_set_domain_bounds: ERROR "u"-component axes not set' - return - endif - call drifters_comm_set_domain(self%comm, domain, self%xu, self%yu, backoff_x, backoff_y) - if(.not.allocated(self%xv) .or. .not.allocated(self%yv)) then - ermesg = 'drifters_set_domain_bounds: ERROR "v"-component axes not set' - return - endif - if(allocated(self%xw) .and. allocated(self%yw)) then - call drifters_comm_set_domain(self%comm, domain, self%xv, self%yv, backoff_x, backoff_y) - endif - - - end subroutine drifters_set_domain_bounds - - !============================================================================ - !> @brief Interpolates positions onto longitude/latitude grid. - !! @details In many cases, the integrated positions will not be longitudes or latitudes. This call - !! can be ionvoked to recover the longitude/latitude positions from the "logical" positions. - subroutine drifters_positions2lonlat(self, positions, & - & x1, y1, geolon1, & - & x2, y2, geolat2, & - & lons, lats, & - & ermesg) - - type(drifters_type) :: self !< Opaque data structure. - ! Input positions - real, intent(in) :: positions(:,:) !< Logical positions. - ! Input mesh - real, intent(in) :: x1(:) !< X-axis of "geolon1" field. - real, intent(in) :: y1(:) !< Y-axis of "geolon1" field. - real, intent(in) :: geolon1(:,:) !< Y-axis of "geolon1" field. - real, intent(in) :: x2(:) !< X-axis of "geolat2" field. - real, intent(in) :: y2(:) !< Y-axis of "geolat2" field. - real, intent(in) :: geolat2(:,:) !< Latitude field as an array of (x2, y2) - ! Output lon/lat - real, intent(out) :: lons(:) !< Returned longitudes. - real, intent(out) :: lats(:) !< Returned latitudes. - character(len=*), intent(out) :: ermesg !< Error message (if any). - - real fvals(2**self%core%nd), ts(self%core%nd) - integer np, ij(2), ip, ier, n1s(2), n2s(2), i, j, iertot - character(len=10) :: n1_str, n2_str, np_str, iertot_str - - ermesg = '' - lons = -huge(1.) - lats = -huge(1.) - - ! check dimensions - n1s = (/size(x1), size(y1)/) - n2s = (/size(x2), size(y2)/) - if(n1s(1) /= size(geolon1, 1) .or. n1s(2) /= size(geolon1, 2)) then - ermesg = 'drifters_positions2geolonlat: ERROR incompatibles dims between (x1, y1, geolon1)' - return - endif - if(n2s(1) /= size(geolat2, 1) .or. n2s(2) /= size(geolat2, 2)) then - ermesg = 'drifters_positions2geolonlat: ERROR incompatibles dims between (x2, y2, geolat2)' - return - endif - - np = size(positions, 2) - if(size(lons) < np .or. size(lats) < np) then - write(np_str, '(i10)') np - write(n1_str, '(i10)') size(lons) - write(n2_str, '(i10)') size(lats) - ermesg = 'drifters_positions2geolonlat: ERROR size of "lons" ('//trim(n1_str)// & - & ') or "lats" ('//trim(n2_str)//') < '//trim(np_str) - return - endif - - ! Interpolate - iertot = 0 - do ip = 1, np - - ! get longitude - call cld_ntrp_locate_cell(x1, positions(1,ip), i, ier) - iertot = iertot + ier - call cld_ntrp_locate_cell(y1, positions(2,ip), j, ier) - iertot = iertot + ier - ij(1) = i; ij(2) = j; - call cld_ntrp_get_cell_values(n1s, _FLATTEN(geolon1), ij, fvals, ier) - iertot = iertot + ier - ts(1) = (positions(1,ip) - x1(i))/(x1(i+1) - x1(i)) - ts(2) = (positions(2,ip) - y1(j))/(y1(j+1) - y1(j)) - call cld_ntrp_linear_cell_interp(fvals, ts, lons(ip), ier) - iertot = iertot + ier - - ! get latitude - call cld_ntrp_locate_cell(x2, positions(1,ip), i, ier) - iertot = iertot + ier - call cld_ntrp_locate_cell(y2, positions(2,ip), j, ier) - iertot = iertot + ier - ij(1) = i; ij(2) = j; - call cld_ntrp_get_cell_values(n2s, _FLATTEN(geolat2), ij, fvals, ier) - iertot = iertot + ier - ts(1) = (positions(1,ip) - x2(i))/(x2(i+1) - x2(i)) - ts(2) = (positions(2,ip) - y2(j))/(y2(j+1) - y2(j)) - call cld_ntrp_linear_cell_interp(fvals, ts, lats(ip), ier) - iertot = iertot + ier - - enddo - - if(iertot /= 0) then - write(iertot_str, '(i10)') iertot - ermesg = 'drifters_positions2geolonlat: ERROR '//trim(iertot_str)// & - & ' interpolation errors (domain out of bounds?)' - endif - - end subroutine drifters_positions2lonlat - - !============================================================================ - !> @brief Print Runge-Kutta check sums. Useful for debugging only. - subroutine drifters_print_checksums(self, pe, ermesg) - - type(drifters_type) :: self !< Opaque handle. - integer, intent(in), optional :: pe !< Processor element. - character(len=*), intent(out) :: ermesg !< Error message (if any). - - integer, parameter :: i8 = selected_int_kind(13) - integer(i8) :: mold, chksum_pos, chksum_k1, chksum_k2, chksum_k3, chksum_k4 - integer(i8) :: chksum_tot - integer nd, np, me - - ermesg = '' - - if(.not. present(pe)) then - me = _MPP_PE - else - me = pe - endif - - if(me == _MPP_PE) then - - nd = self%core%nd - np = self%core%np - chksum_pos = transfer(sum(sum(self%core%positions(1:nd,1:np),1)), mold) - chksum_k1 = transfer(sum(sum(self%rk4_k1(1:nd,1:np),1)), mold) - chksum_k2 = transfer(sum(sum(self%rk4_k2(1:nd,1:np),1)), mold) - chksum_k3 = transfer(sum(sum(self%rk4_k3(1:nd,1:np),1)), mold) - chksum_k4 = transfer(sum(sum(self%rk4_k4(1:nd,1:np),1)), mold) - chksum_tot = chksum_pos + chksum_k1 + chksum_k2 + chksum_k3 +chksum_k4 - - print *,'==============drifters checksums==========================' - print '(a,i25,a,i6,a,e15.7)','==positions: ', chksum_pos, ' PE=', me, ' time = ', self%time - print '(a,i25,a,i6,a,e15.7)','==k1 : ', chksum_k1, ' PE=', me, ' time = ', self%time - print '(a,i25,a,i6,a,e15.7)','==k2 : ', chksum_k2, ' PE=', me, ' time = ', self%time - print '(a,i25,a,i6,a,e15.7)','==k3 : ', chksum_k3, ' PE=', me, ' time = ', self%time - print '(a,i25,a,i6,a,e15.7)','==k4 : ', chksum_k4, ' PE=', me, ' time = ', self%time - print '(a,i25,a,i6,a,e15.7)','==total : ', chksum_tot, ' PE=', me, ' time = ', self%time - - endif - - end subroutine drifters_print_checksums - - subroutine drifters_reset_rk4(self, ermesg) - type(drifters_type) :: self - character(len=*), intent(out) :: ermesg - - integer ier, nd - - ermesg = '' - - if(size(self%rk4_k1, 2) < self%core%np) then - deallocate(self%rk4_k1, stat=ier) - allocate(self%rk4_k1(self%core%nd, self%core%npdim)) - self%rk4_k1 = 0 - endif - if(size(self%rk4_k2, 2) < self%core%np) then - deallocate(self%rk4_k2, stat=ier) - allocate(self%rk4_k2(self%core%nd, self%core%npdim)) - self%rk4_k2 = 0 - endif - if(size(self%rk4_k3, 2) < self%core%np) then - deallocate(self%rk4_k3, stat=ier) - allocate(self%rk4_k3(self%core%nd, self%core%npdim)) - self%rk4_k3 = 0 - endif - if(size(self%rk4_k4, 2) < self%core%np) then - deallocate(self%rk4_k4, stat=ier) - allocate(self%rk4_k4(self%core%nd, self%core%npdim)) - self%rk4_k4 = 0 - endif - - if(size(self%remove) < self%core%np) then - deallocate(self%remove, stat=ier) - allocate(self%remove(self%core%npdim)) - self%remove = .FALSE. - endif - - if(size(self%temp_pos, 2) < self%core%np) then - deallocate(self%temp_pos, stat=ier) - nd = size(self%input%velocity_names) - allocate(self%temp_pos(nd, self%core%npdim)) - self%temp_pos = -huge(1.) - endif - - end subroutine drifters_reset_rk4 - -end module drifters_mod -!> @} -! close documentation grouping diff --git a/drifters/include/drifters_comm.inc b/drifters/include/drifters_comm.inc deleted file mode 100644 index 5319e19934..0000000000 --- a/drifters/include/drifters_comm.inc +++ /dev/null @@ -1,776 +0,0 @@ -!*********************************************************************** -!* GNU Lesser General Public License -!* -!* This file is part of the GFDL Flexible Modeling System (FMS). -!* -!* FMS is free software: you can redistribute it and/or modify it under -!* the terms of the GNU Lesser General Public License as published by -!* the Free Software Foundation, either version 3 of the License, or (at -!* your option) any later version. -!* -!* FMS is distributed in the hope that it will be useful, but WITHOUT -!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -!* for more details. -!* -!* You should have received a copy of the GNU Lesser General Public -!* License along with FMS. If not, see . -!*********************************************************************** -#include "fms_switches.h" - -!> @defgroup drifters_comm_mod drifters_comm_mod -!> @ingroup drifters -!> @brief Routines and types to update drifter positions across processor domains - -module drifters_comm_mod - -#ifdef _SERIAL - -#define _TYPE_DOMAIN2D integer -#define _NULL_PE 0 - -#else - - use mpp_mod, only : NULL_PE, FATAL, NOTE, mpp_error, mpp_pe, mpp_npes - use mpp_mod, only : mpp_root_pe - use mpp_mod, only : mpp_send, mpp_recv, mpp_sync_self - use mpp_mod, only : COMM_TAG_1, COMM_TAG_2, COMM_TAG_3, COMM_TAG_4 - use mpp_domains_mod, only : domain2D - use mpp_domains_mod, only : mpp_get_neighbor_pe, mpp_define_domains, mpp_get_layout - use mpp_domains_mod, only : mpp_get_compute_domain, mpp_get_data_domain - use mpp_domains_mod, only : NORTH, SOUTH, EAST, WEST, CYCLIC_GLOBAL_DOMAIN - use mpp_domains_mod, only : NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST - -#define _TYPE_DOMAIN2D type(domain2d) -#define _NULL_PE NULL_PE - -#endif - - use drifters_core_mod, only: drifters_core_type, drifters_core_remove_and_add, drifters_core_set_positions - - implicit none - private - - public :: drifters_comm_type, drifters_comm_new, drifters_comm_del, drifters_comm_set_pe_neighbors - public :: drifters_comm_set_domain, drifters_comm_update, drifters_comm_gather - - !> Type for drifter communication between PE's - !> @ingroup drifters_comm_mod - type :: drifters_comm_type - real :: xcmin !< compute domain - real :: xcmax !< compute domain - real :: ycmin !< compute domain - real :: ycmax !< compute domain - real :: xdmin !< data domain - real :: xdmax !< data domain - real :: ydmin !< data domain - real :: ydmax !< data domain - real :: xgmin !< global valid min/max - real :: xgmax !< global valid min/max - real :: ygmin !< global valid min/max - real :: ygmax !< global valid min/max - logical :: xperiodic !< x/y period (can be be nearly infinite) - logical :: yperiodic !< x/y period (can be be nearly infinite) - integer :: pe_N !< neighbor domains - integer :: pe_S !< neighbor domains - integer :: pe_E !< neighbor domains - integer :: pe_W !< neighbor domains - integer :: pe_NE !< neighbor domains - integer :: pe_SE !< neighbor domains - integer :: pe_SW !< neighbor domains - integer :: pe_NW !< neighbor domains - integer :: pe_beg !< starting/ending pe, set this to a value /= 0 if running concurrently - integer :: pe_end !< starting/ending pe, set this to a value /= 0 if running concurrently - end type drifters_comm_type - -contains - -!> @addtogroup drifters_comm_mod -!> @{ -!=============================================================================== - !> @brief Initializes default values for @ref drifters_comm_type in self - subroutine drifters_comm_new(self) - type(drifters_comm_type) :: self !< A new @ref drifters_comm_type - - self%xcmin = -huge(1.); self%xcmax = +huge(1.) - self%ycmin = -huge(1.); self%ycmax = +huge(1.) - - self%xdmin = -huge(1.); self%xdmax = +huge(1.) - self%ydmin = -huge(1.); self%ydmax = +huge(1.) - - self%xgmin = -huge(1.); self%xgmax = +huge(1.) - self%ygmin = -huge(1.); self%ygmax = +huge(1.) - - self%xperiodic = .FALSE.; self%yperiodic = .FALSE. - - self%pe_N = _NULL_PE - self%pe_S = _NULL_PE - self%pe_E = _NULL_PE - self%pe_W = _NULL_PE - self%pe_NE = _NULL_PE - self%pe_SE = _NULL_PE - self%pe_SW = _NULL_PE - self%pe_NW = _NULL_PE - - self%pe_beg = 0 - self%pe_end = -1 - - - end subroutine drifters_comm_new - -!=============================================================================== - !> @brief Reset data in a given @ref drifters_comm_type to defaults - subroutine drifters_comm_del(self) - type(drifters_comm_type) :: self !< A @ref drifters_comm_type to reset - - ! nothing to deallocate - call drifters_comm_new(self) - - end subroutine drifters_comm_del - -!=============================================================================== - !> @brief Set data domain bounds. - subroutine drifters_comm_set_data_bounds(self, xmin, ymin, xmax, ymax) - ! Set data domain bounds. - type(drifters_comm_type) :: self - real, intent(in) :: xmin, ymin, xmax, ymax - - self%xdmin = max(xmin, self%xdmin) - self%xdmax = min(xmax, self%xdmax) - self%ydmin = max(ymin, self%ydmin) - self%ydmax = min(ymax, self%ydmax) - - end subroutine drifters_comm_set_data_bounds - -!=============================================================================== - !> @brief Set compute domain bounds. - subroutine drifters_comm_set_comp_bounds(self, xmin, ymin, xmax, ymax) - ! Set compute domain bounds. - type(drifters_comm_type) :: self - real, intent(in) :: xmin, ymin, xmax, ymax - - self%xcmin = max(xmin, self%xcmin) - self%xcmax = min(xmax, self%xcmax) - self%ycmin = max(ymin, self%ycmin) - self%ycmax = min(ymax, self%ycmax) - - end subroutine drifters_comm_set_comp_bounds - -!=============================================================================== - !> @brief Set neighboring pe numbers. - subroutine drifters_comm_set_pe_neighbors(self, domain) - ! Set neighboring pe numbers. - type(drifters_comm_type) :: self !< Drifters communication type to set pe numbers for - _TYPE_DOMAIN2D, intent(inout) :: domain !< domain to get neighboring PE's from - -#ifndef _SERIAL -! parallel code - - integer :: pe_N, pe_S, pe_E, pe_W, pe_NE, pe_SE, pe_SW, pe_NW - - call mpp_get_neighbor_pe(domain, NORTH , pe_N ) - call mpp_get_neighbor_pe(domain, NORTH_EAST, pe_NE) - call mpp_get_neighbor_pe(domain, EAST , pe_E ) - call mpp_get_neighbor_pe(domain, SOUTH_EAST, pe_SE) - call mpp_get_neighbor_pe(domain, SOUTH , pe_S ) - call mpp_get_neighbor_pe(domain, SOUTH_WEST, pe_SW) - call mpp_get_neighbor_pe(domain, WEST , pe_W ) - call mpp_get_neighbor_pe(domain, NORTH_WEST, pe_NW) - - if(pe_N /= self%pe_N .and. self%pe_N == _NULL_PE) then - self%pe_N = pe_N - else if(pe_N /= self%pe_N ) then - call mpp_error( FATAL, 'drifters_comm_set_pe_neighbors: NORTH PE changed!.') - endif - if(pe_NE /= self%pe_NE .and. self%pe_NE == _NULL_PE) then - self%pe_NE = pe_NE - else if(pe_NE /= self%pe_NE) then - call mpp_error( FATAL, 'drifters_comm_set_pe_neighbors: NORTH-EAST PE changed!.') - endif - if(pe_E /= self%pe_E .and. self%pe_E == _NULL_PE) then - self%pe_E = pe_E - else if(pe_E /= self%pe_E ) then - call mpp_error( FATAL, 'drifters_comm_set_pe_neighbors: EAST PE changed!.') - endif - if(pe_SE /= self%pe_SE .and. self%pe_SE == _NULL_PE) then - self%pe_SE = pe_SE - else if(pe_SE /= self%pe_SE) then - call mpp_error( FATAL, 'drifters_comm_set_pe_neighbors: SOUTH-EAST PE changed!.') - endif - if(pe_S /= self%pe_S .and. self%pe_S == _NULL_PE) then - self%pe_S = pe_S - else if(pe_S /= self%pe_S ) then - call mpp_error( FATAL, 'drifters_comm_set_pe_neighbors: SOUTH PE changed!.') - endif - if(pe_SW /= self%pe_SW .and. self%pe_SW == _NULL_PE) then - self%pe_SW = pe_SW - else if(pe_SW /= self%pe_SW) then - call mpp_error( FATAL, 'drifters_comm_set_pe_neighbors: SOUTH-WEST PE changed!.') - endif - if(pe_W /= self%pe_W .and. self%pe_W == _NULL_PE) then - self%pe_W = pe_W - else if(pe_W /= self%pe_W ) then - call mpp_error( FATAL, 'drifters_comm_set_pe_neighbors: WEST PE changed!.') - endif - if(pe_NW /= self%pe_NW .and. self%pe_NW == _NULL_PE) then - self%pe_NW = pe_NW - else if(pe_NW /= self%pe_NW) then - call mpp_error( FATAL, 'drifters_comm_set_pe_neighbors: NORTH-WEST PE changed!.') - endif - -#endif -! end of parallel code - - end subroutine drifters_comm_set_pe_neighbors - -!=============================================================================== - !> @brief Set boundaries of domain and compute neighbors. This method can be called - !! multiple times; the data domain will just be the intersection (overlap) of - !! all domains (e.g domain_u, domain_v, etc). - subroutine drifters_comm_set_domain(self, domain, x, y, backoff_x, backoff_y) - ! Set boundaries of domain and compute neighbors. This method can be called - ! multiple times; the data domain will just be the intersection (overlap) of - ! all domains (e.g domain_u, domain_v, etc). - type(drifters_comm_type) :: self - _TYPE_DOMAIN2D, intent(inout) :: domain - real, intent(in) :: x(:) !< global axes - real, intent(in) :: y(:) !< global axes - integer, intent(in) :: backoff_x !< >=0, data domain is reduced by "backoff_x,y" indices in x, resp. y - integer, intent(in) :: backoff_y !< >=0, data domain is reduced by "backoff_x,y" indices in x, resp. y - - ! compute/data domain start/end indices - integer :: isc !< compute domain start/end indices - integer :: iec !< compute domain start/end indices - integer :: jsc !< compute domain start/end indices - integer :: jec !< compute domain start/end indices - integer :: isd !< data domain start/end indices - integer :: ied !< data domain start/end indices - integer :: jsd !< data domain start/end indices - integer :: jed !< data domain start/end indices - integer nx, ny, hx, hy, bckf_x, bckf_y, halox, haloy - real dx, dy, xdmin, xdmax, ydmin, ydmax - -#ifdef _SERIAL - integer :: ibnds(1) - - ibnds = lbound(x); isc = ibnds(1) - ibnds = ubound(x); iec = ibnds(1) - 1 - ibnds = lbound(y); jsc = ibnds(1) - ibnds = ubound(y); jec = ibnds(1) - 1 -#else - call mpp_get_compute_domain( domain, isc, iec, jsc, jec ) -#endif - - self%xcmin = max(x(isc), self%xcmin) - self%xcmax = min(x(iec), self%xcmax) - self%ycmin = max(y(jsc), self%ycmin) - self%ycmax = min(y(jec), self%ycmax) - - nx = iec - isc + 1 - ny = jec - jsc + 1 - -#ifdef _SERIAL - isd = 1; ied = size(x); jsd = 1; jed = size(y) -#else - call mpp_get_data_domain ( domain, isd, ied, jsd, jed ) -#endif - - hx = max(ied-iec, isc-isd) - hy = max(jed-jec, jsc-jsd) - bckf_x = min(backoff_x, hx) - bckf_y = min(backoff_y, hy) - - halox = max(0, hx - bckf_x) - haloy = max(0, hy - bckf_y) - - if(isd < 1) then - dx = x(2) - x(1) - xdmin = self%xcmin - dx*halox - else - xdmin = x(isd+bckf_x) - endif - - if(ied > nx) then - dx = x(nx) - x(nx-1) - xdmax = self%xcmax + dx*halox - else - xdmax = x(ied-bckf_x) - endif - - if(jsd < 1) then - dy = y(2) - y(1) - ydmin = self%ycmin - dy*haloy - else - ydmin = y(jsd+bckf_y) - endif - - if(jed > ny) then - dy = y(ny) - y(ny-1) - ydmax = self%ycmax + dy*haloy - else - ydmax = y(jed-bckf_y) - endif - - self%xdmin = max(xdmin, self%xdmin) - self%ydmin = max(ydmin, self%ydmin) - self%xdmax = min(xdmax, self%xdmax) - self%ydmax = min(ydmax, self%ydmax) - - call drifters_comm_set_pe_neighbors(self, domain) - - end subroutine drifters_comm_set_domain - -!=============================================================================== - !> Updates drifter communication - subroutine drifters_comm_update(self, drfts, new_positions, & - & comm, remove, max_add_remove) -#ifndef _SERIAL - use mpi -#endif - - type(drifters_comm_type) :: self - type(drifters_core_type) :: drfts - real, intent(inout) :: new_positions(:,:) - integer, intent(in), optional :: comm !< MPI communicator - logical, intent(in), optional :: remove(:) !< Set to True for particles that should be removed - integer, intent(in), optional :: max_add_remove !< max no of particles to add/remove - -#ifdef _SERIAL -! serial code - - drfts%positions(:, 1:drfts%np) = new_positions(:, 1:drfts%np) - return - -#else -! parallel code - integer nd, np, nar_est, ip, neigh_pe, irem, pe, npes, ntuples - integer ntuples_tot, ndata, mycomm -#ifdef _USE_MPI - integer ier -#endif - integer, allocatable :: iadd(:) - integer, allocatable :: table_recv(:), table_send(:) - real , allocatable :: data_recv(:,:), data_send(:,:) - integer, allocatable :: indices_to_remove(:) - integer, allocatable :: ids_to_add(:) - real , allocatable :: positions_to_add(:,:) - real :: x, y, xold, yold - character(len=128) :: ermsg, notemsg - logical :: is_present - integer :: id, j, k, m, n, el - logical :: crossed_W, crossed_E, crossed_S, crossed_N - logical :: was_in_compute_domain, left_domain - - mycomm = MPI_COMM_WORLD - if( present(comm) ) mycomm = comm - - nd = drfts%nd - np = size(new_positions,2) - if(np > 0 .and. nd < 2) call mpp_error( FATAL, & - & 'drifters_comm_update: number of dimensions must be 2 or higher.' ) - - nar_est = 100 - if(present(max_add_remove)) nar_est = max(1, max_add_remove) - - pe = mpp_pe() - npes = mpp_npes() - - ! assume pe list is contiguous, self%pe_beg...self%pe_end - allocate(iadd(self%pe_beg:self%pe_end)) - allocate(table_recv(self%pe_beg:self%pe_end)) - allocate(table_send(self%pe_beg:self%pe_end)) - allocate(data_recv(nar_est*(1+nd), self%pe_beg:self%pe_end)) - allocate(data_send(nar_est*(1+nd), self%pe_beg:self%pe_end)) - allocate(indices_to_remove(nar_est)) - - table_send = 0 - table_recv = 0 - data_send = 0 - data_recv = 0 - - iadd = 0 - irem = 0 - do ip = 1, np - x = new_positions(1, ip) - y = new_positions(2, ip) - xold = drfts%positions(1, ip) - yold = drfts%positions(2, ip) - - if( xoldself%xcmax .or. & - & yoldself%ycmax ) then - was_in_compute_domain = .FALSE. - else - was_in_compute_domain = .TRUE. - endif - - ! check if drifters crossed compute domain boundary - - crossed_W = .FALSE. - crossed_E = .FALSE. - crossed_S = .FALSE. - crossed_N = .FALSE. - if( was_in_compute_domain .and. & - & (xself%xcmin) ) crossed_W = .TRUE. - if( was_in_compute_domain .and. & - & (x>self%xcmax) .and. (xoldself%ycmin) ) crossed_S = .TRUE. - if( was_in_compute_domain .and. & - & (y>self%ycmax) .and. (yold nar_est) then - write(notemsg, '(a,i4,a,i4,a)') 'drifters_comm_update: exceeded nar_est (', & - & iadd(neigh_pe),'>',nar_est,').' - call mpp_error( FATAL, notemsg) - endif - table_send(neigh_pe) = table_send(neigh_pe) + 1 - k = ( iadd(neigh_pe)-1 )*(1+nd) + 1 - data_send(k , neigh_pe) = drfts%ids(ip) - data_send(k+1:k+nd, neigh_pe) = new_positions(:,ip) - endif - - ! check if drifters left data domain - - left_domain = .FALSE. - if( (xself%xdmax .and. (self%pe_E/=pe)) .or. & - & (yself%ydmax .and. (self%pe_N/=pe)) ) then - left_domain = .TRUE. - endif - - ! remove if particle was tagged as such - - if(present(remove)) then - if(remove(ip)) left_domain = .TRUE. - endif - - if(left_domain) then - irem = irem + 1 - if(irem > nar_est) then - write(notemsg, '(a,i4,a,i4,a)') 'drifters_comm_update: exceeded nar_est (',& - & irem,'>',nar_est,').' - call mpp_error( FATAL, notemsg) - endif - indices_to_remove(irem) = ip - endif - - enddo - - - ! update drifters' positions (remove whatever needs to be removed later) - call drifters_core_set_positions(drfts, new_positions, ermsg) - if(ermsg/='') call mpp_error( FATAL, ermsg) - - ! fill in table_recv from table_send. table_send contains the - ! number of tuples that will be sent to another pe. table_recv - ! will contain the number of tuples to be received. The indices - ! of table_send refer to the pe where the tuples should be sent to; - ! the indices of table_recv refer to the pe number - ! (self%pe_beg..self%pe_end) from - ! which the tuple should be received from. - ! - ! table_send(to_pe) = ntuples; table_recv(from_pe) = ntuples - - ! the following is a transpose operation - ! table_send(m)[pe] -> table_recv(pe)[m] - do m = self%pe_beg, self%pe_end -#ifdef _USE_MPI - call MPI_Scatter (table_send , 1, MPI_INTEGER, & - & table_recv(m), 1, MPI_INTEGER, & - & m, mycomm, ier ) -#else - if(pe==m) then - do k = self%pe_beg, self%pe_end - call mpp_send(table_send(k), plen=1, to_pe=k, tag=COMM_TAG_1) - enddo - endif - call mpp_recv(table_recv(m), glen=1, from_pe=m, tag=COMM_TAG_1) -#endif - enddo - - ! communicate new positions. data_send is an array of size n*(nd+1) times npes. - ! Each column j of data_send contains the tuple (id, x, y, ..) to be sent to pe=j. - ! Inversely, data_recv's column j contains tuples (id, x, y,..) received from pe=j. - do m = self%pe_beg, self%pe_end - ntuples = table_send(m) - ndata = ntuples*(nd+1) - ! should be able to send ndata? -#ifdef _USE_MPI - call MPI_Scatter (data_send , nar_est*(1+nd), MPI_REAL8, & - & data_recv(1,m), nar_est*(1+nd), MPI_REAL8, & - & m, mycomm, ier ) -#else - if(pe==m) then - do k = self%pe_beg, self%pe_end - call mpp_send(data_send(1,k), plen=nar_est*(1+nd), to_pe=k, tag=COMM_TAG_2) - enddo - endif - call mpp_recv(data_recv(1,m), glen=nar_est*(1+nd), from_pe=m, tag=COMM_TAG_2) -#endif - enddo - - ! total number of tuples will determine size of ids_to_add/positions_to_add - ntuples_tot = 0 - do m = self%pe_beg, self%pe_end - ntuples_tot = ntuples_tot + table_recv(m) - enddo - - allocate(positions_to_add(nd, ntuples_tot)) - allocate( ids_to_add( ntuples_tot)) - - ! fill positions_to_add and ids_to_add. - k = 0 - do m = self%pe_beg, self%pe_end - ! get ids/positions coming from all pes - do n = 1, table_recv(m) - ! iterate over all ids/positions coming from pe=m - el = (n-1)*(nd+1) + 1 - id = int(data_recv(el, m)) - ! only add if id not already present in drfts - ! this can happen if a drifter meanders about - ! the compute domain boundary - is_present = .false. - do j = 1, drfts%np - if(id == drfts%ids(j)) then - is_present = .true. - write(notemsg, '(a,i4,a)') 'Drifter ', id, ' already advected (will not be added).' - call mpp_error(NOTE, notemsg) - exit - endif - enddo - if(.not. is_present) then - k = k + 1 - ids_to_add(k) = id - - positions_to_add(1:nd, k) = data_recv(el+1:el+nd, m) - - endif - enddo - enddo - - ! remove and add - if(irem > 0 .or. k > 0) then - write(notemsg, '(i4,a,i4,a)') irem, ' drifter(s) will be removed, ', k,' will be added' - call mpp_error(NOTE, notemsg) -!!$ if(k>0) print *,'positions to add ', positions_to_add(:,1:k) -!!$ if(irem>0) print *,'ids to remove: ', indices_to_remove(1:irem) - endif - call drifters_core_remove_and_add(drfts, indices_to_remove(1:irem), & - & ids_to_add(1:k), positions_to_add(:,1:k), ermsg) - if(ermsg/='') call mpp_error( FATAL, ermsg) - -#ifndef _USE_MPI - ! make sure unbuffered mpp_isend call returned before deallocating - call mpp_sync_self() -#endif - - deallocate(ids_to_add) - deallocate(positions_to_add) - - deallocate(iadd) - deallocate(table_recv) - deallocate(table_send) - deallocate(data_recv) - deallocate(data_send) - deallocate(indices_to_remove) - -#endif -! end of parallel code - - end subroutine drifters_comm_update - -!=============================================================================== - subroutine drifters_comm_gather(self, drfts, dinp, & - & lons, lats, do_save_lonlat, & - & filename, & - & root, mycomm) - -#ifndef _SERIAL - use mpi -#endif - use drifters_input_mod, only : drifters_input_type, drifters_input_save - - type(drifters_comm_type) :: self - type(drifters_core_type) :: drfts - type(drifters_input_type) :: dinp - real, intent(in) :: lons(:), lats(:) - logical, intent(in) :: do_save_lonlat - character(len=*), intent(in) :: filename - integer, intent(in), optional :: root !< root pe - integer, intent(in), optional :: mycomm !< MPI communicator - - character(len=128) :: ermesg - -#ifdef _SERIAL -! serial code - - dinp%ids(1:drfts%np) = drfts%ids(1:drfts%np) - dinp%positions(:, 1:drfts%np) = drfts%positions(:, 1:drfts%np) - - if(do_save_lonlat) then - - call drifters_input_save(dinp, filename=filename, & - & geolon=lons, geolat=lats, ermesg=ermesg) - - else - - call drifters_input_save(dinp, filename=filename, ermesg=ermesg) - - endif - -#else -! parallel code - - - integer :: npf, ip, comm, root_pe, pe, npes, nd, np, npdim, npmax, ier, nptot - integer :: i, j, k, kk - integer, allocatable :: nps(:) - real :: x, y - real, allocatable :: lons0(:), lats0(:), recvbuf(:,:) - real :: data(drfts%nd+3, drfts%np) - - comm = MPI_COMM_WORLD - if(present(mycomm)) comm = mycomm - - root_pe = mpp_root_pe() - if(present(root)) root_pe = root - - pe = mpp_pe() - npes = mpp_npes() - - nd = drfts%nd - np = drfts%np - npdim = drfts%npdim - - allocate(nps(self%pe_beg:self%pe_end)) - nps = 0 - - ! npf= number of drifters in compute domain - - npf = 0 - do ip = 1, np - x = drfts%positions(1, ip) - y = drfts%positions(2, ip) - if( x <= self%xcmax .and. x >= self%xcmin .and. & - & y <= self%ycmax .and. y >= self%ycmin) then - npf = npf + 1 - data(1 , npf) = real(drfts%ids(ip)) - data(1+1:1+nd, npf) = drfts%positions(:, ip) - data( 2+nd, npf) = lons(ip) - data( 3+nd, npf) = lats(ip) - endif - enddo - - ! gather number of drifters -#ifdef _USE_MPI - call mpi_gather(npf, 1, MPI_INT, & - & nps, 1, MPI_INT, & - & root_pe, comm, ier) - !!if(ier/=0) ermesg = 'drifters_write_restart: ERROR while gathering "npf"' -#else - call mpp_send(npf, plen=1, to_pe=root_pe, tag=COMM_TAG_3) - if(pe==root_pe) then - do i = self%pe_beg, self%pe_end - call mpp_recv(nps(i), glen=1, from_pe=i, tag=COMM_TAG_3) - enddo - endif -#endif - - ! Now we know the max number of drifters to expect from each PE, so allocate - ! recvbuf (first dim will be zero on all PEs except root). - - ! allocate recvbuf to receive all the data on root PE, strided by npmax*(nd+3) - npmax = maxval(nps) - allocate(recvbuf(npmax*(nd+3), self%pe_beg:self%pe_end)) - recvbuf = -1 - - ! Each PE sends data to recvbuf on root_pe. -#ifdef _USE_MPI - call mpi_gather( data , npf*(nd+3), MPI_REAL8, & - & recvbuf, npmax*(nd+3), MPI_REAL8, & - & root_pe, comm, ier) - !!if(ier/=0) ermesg = 'drifters_write_restart: ERROR while gathering "data"' -#else - if(npf > 0) call mpp_send(data(1,1), plen=npf*(nd+3), to_pe=root_pe, tag=COMM_TAG_4) - if(pe==root_pe) then - do i = self%pe_beg, self%pe_end - if(nps(i) > 0) call mpp_recv(recvbuf(1, i), glen=nps(i)*(nd+3), from_pe=i, tag=COMM_TAG_4) - enddo - endif -#endif - - ! Set positions and ids - if(pe == root_pe) then - ! check dims - nptot = sum(nps) ! total number of drifters, across al PEs - if(nptot /= size(dinp%ids)) then - deallocate(dinp%ids , stat=ier) - deallocate(dinp%positions, stat=ier) - allocate(dinp%ids(nptot)) - allocate(dinp%positions(nd, nptot)) - dinp%ids = -1 - dinp%positions = -huge(1.) - endif - - allocate(lons0(nptot), lats0(nptot)) - - ! Set the new positions/ids in dinp, on PE=root. Also set - ! lons/lats, these arrays will hold garbage if x1, y1, etc. were - ! not passed as subroutine arguments, that's ok 'cause we won't - ! save them. - j = 1 - do i = self%pe_beg, self%pe_end - do k = 1, nps(i) - kk = (nd+3)*(k-1) - dinp%ids(j) = int(recvbuf(kk+1 , i)) - dinp%positions(1:nd, j) = recvbuf(kk+1+1:kk+1+nd, i) - lons0(j) = recvbuf(kk+2+nd, i) - lats0(j) = recvbuf(kk+3+nd, i) - j = j + 1 - enddo - enddo - - if(do_save_lonlat) then - - call drifters_input_save(dinp, filename=filename, & - & geolon=lons0, geolat=lats0, ermesg=ermesg) - - else - - call drifters_input_save(dinp, filename=filename, ermesg=ermesg) - - endif - - deallocate(lons0, lats0) - - endif - -#ifndef _USE_MPI - call mpp_sync_self() -#endif - deallocate(nps , stat=ier) - deallocate(recvbuf, stat=ier) - -#endif -! _end of parallel code - - end subroutine drifters_comm_gather - - -end module drifters_comm_mod - -!=============================================================================== -!=============================================================================== diff --git a/drifters/include/drifters_core.inc b/drifters/include/drifters_core.inc deleted file mode 100644 index c25dd85e54..0000000000 --- a/drifters/include/drifters_core.inc +++ /dev/null @@ -1,279 +0,0 @@ -!*********************************************************************** -!* GNU Lesser General Public License -!* -!* This file is part of the GFDL Flexible Modeling System (FMS). -!* -!* FMS is free software: you can redistribute it and/or modify it under -!* the terms of the GNU Lesser General Public License as published by -!* the Free Software Foundation, either version 3 of the License, or (at -!* your option) any later version. -!* -!* FMS is distributed in the hope that it will be useful, but WITHOUT -!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -!* for more details. -!* -!* You should have received a copy of the GNU Lesser General Public -!* License along with FMS. If not, see . -!*********************************************************************** -!> @defgroup drifters_core_mod drifters_core_mod -!> @ingroup drifters -!> @brief Handles the mechanics for adding and removing drifters - -module drifters_core_mod - use platform_mod - implicit none - private - - public :: drifters_core_type, drifters_core_new, drifters_core_del, drifters_core_set_ids - public :: drifters_core_remove_and_add, drifters_core_set_positions, assignment(=) - public :: drifters_core_print, drifters_core_resize - - ! Globals - integer, parameter, private :: MAX_STR_LEN = 128 -! Include variable "version" to be written to log file. -#include - - !> @brief Core data needed for drifters. - !! Be sure to update drifters_core_new, drifters_core_del and drifters_core_copy_new - !! when adding members. - !> @ingroup drifters_core_mod - type drifters_core_type - integer(kind=i8_kind) :: it !< time index - integer :: nd !< number of dimensions - integer :: np !< number of particles (drifters) - integer :: npdim !< max number of particles (drifters) - integer, allocatable :: ids(:) !< particle id number - real , allocatable :: positions(:,:) - end type drifters_core_type - - !> @brief Assignment override for @ref drifters_core_type - !> @ingroup drifters_core_mod - interface assignment(=) - module procedure drifters_core_copy_new - end interface - -contains - -!> @addtogroup drifters_core_mod -!> @{ -!############################################################################### - !> Create a new @ref drifters_core_type - subroutine drifters_core_new(self, nd, npdim, ermesg) - type(drifters_core_type) :: self !< @ref drifters_core_type to create - integer, intent(in) :: nd - integer, intent(in) :: npdim - character(*), intent(out) :: ermesg !< Error message string - integer ier, iflag, i - ermesg = '' - ier = 0 - - call drifters_core_del(self, ermesg) - - allocate(self%positions(nd, npdim), stat=iflag) - if(iflag/=0) ier = ier + 1 - self%positions = 0. - - allocate(self%ids(npdim), stat=iflag) - if(iflag/=0) ier = ier + 1 - self%ids = (/(i, i=1,npdim)/) - - self%nd = nd - self%npdim = npdim - - if(ier/=0) ermesg = 'drifters::ERROR in drifters_core_new' - end subroutine drifters_core_new - - !############################################################################### - !> Deallocates the given @ref drifters_core_type - subroutine drifters_core_del(self, ermesg) - type(drifters_core_type) :: self !< @ref drifters_core_type to delete - character(*), intent(out) :: ermesg !< Error message string - integer ier, iflag - ermesg = '' - ier = 0 - self%it = 0 - self%nd = 0 - self%np = 0 - iflag = 0 - if(allocated(self%positions)) deallocate(self%positions, stat=iflag) - if(iflag/=0) ier = ier + 1 - if(allocated(self%ids)) deallocate(self%ids, stat=iflag) - if(iflag/=0) ier = ier + 1 - - if(ier/=0) ermesg = 'drifters::ERROR in drifters_core_del' - end subroutine drifters_core_del - - !############################################################################### - subroutine drifters_core_copy_new(new_instance, old_instance) - - type(drifters_core_type), intent(inout) :: new_instance - type(drifters_core_type), intent(in) :: old_instance - - character(len=MAX_STR_LEN) :: ermesg - - ermesg = '' - call drifters_core_del(new_instance, ermesg) - if(ermesg/='') return - ! this should provide the right behavior for both pointers and allocatables - new_instance%it = old_instance%it - new_instance%nd = old_instance%nd - new_instance%np = old_instance%np - new_instance%npdim = old_instance%npdim - allocate(new_instance%ids( size(old_instance%ids) )) - new_instance%ids = old_instance%ids - allocate(new_instance%positions( size(old_instance%positions,1), & - & size(old_instance%positions,2) )) - new_instance%positions = old_instance%positions - - end subroutine drifters_core_copy_new - !############################################################################### - subroutine drifters_core_resize(self, npdim, ermesg) - type(drifters_core_type) :: self - integer, intent(in) :: npdim !< new max value - character(*), intent(out) :: ermesg - integer ier, iflag, i - - real , allocatable :: positions(:,:) - integer, allocatable :: ids(:) - - ermesg = '' - ier = 0 - if(npdim <= self%npdim) return - - ! temps - allocate(positions(self%nd, self%np), stat=iflag) - allocate( ids(self%np), stat=iflag) - - positions = self%positions(:, 1:self%np) - ids = self%ids(1:self%np) - - deallocate(self%positions, stat=iflag) - deallocate(self%ids , stat=iflag) - - allocate(self%positions(self%nd, npdim), stat=iflag) - allocate(self%ids(npdim), stat=iflag) - self%positions = 0.0 - ! default id numbers - self%ids = (/ (i, i=1,npdim) /) - self%positions(:, 1:self%np) = positions - self%npdim = npdim - - if(ier/=0) ermesg = 'drifters::ERROR in drifters_core_resize' - end subroutine drifters_core_resize - -!############################################################################### - subroutine drifters_core_set_positions(self, positions, ermesg) - type(drifters_core_type) :: self - real, intent(in) :: positions(:,:) - character(*), intent(out) :: ermesg - integer ier !, iflag - ermesg = '' - ier = 0 - self%np = min(self%npdim, size(positions, 2)) - self%positions(:,1:self%np) = positions(:,1:self%np) - self%it = self%it + 1 - if(ier/=0) ermesg = 'drifters::ERROR in drifters_core_set_positions' - end subroutine drifters_core_set_positions - -!############################################################################### - subroutine drifters_core_set_ids(self1, ids1, ermesg1) - type(drifters_core_type) :: self1 - integer, intent(in) :: ids1(:) - character(*), intent(out) :: ermesg1 - integer ier, np !, iflag - ermesg1 = '' - ier = 0 - np = min(self1%npdim, size(ids1)) - self1%ids(1:np) = ids1(1:np) - if(ier/=0) ermesg1 = 'drifters::ERROR in drifters_core_set_ids' - end subroutine drifters_core_set_ids - -!############################################################################### -subroutine drifters_core_remove_and_add(self, indices_to_remove_in, & - & ids_to_add, positions_to_add, & - & ermesg) - type(drifters_core_type) :: self - integer, intent(in ) :: indices_to_remove_in(:) - integer, intent(in ) :: ids_to_add(:) - real , intent(in ) :: positions_to_add(:,:) - character(*), intent(out) :: ermesg - integer ier, np_add, np_remove, i, j, n_diff !, iflag - integer indices_to_remove(size(indices_to_remove_in)) - external qksrt_quicksort - ermesg = '' - ier = 0 - - ! copy, required so we can have indices_to_remove_in intent(in) - indices_to_remove = indices_to_remove_in - np_remove = size(indices_to_remove) - np_add = size(ids_to_add, 1) - n_diff = np_add - np_remove - - ! cannot remove more than there are elements - if(self%np + n_diff < 0) then - ermesg = 'drifters::ERROR attempting to remove more elements than there are elements in '// & - &'drifters_core_remove_and_add' - return - endif - - ! check for overflow, and resize if necessary - if(self%np + n_diff > self%npdim) & - & call drifters_core_resize(self, int(1.2*(self%np + n_diff))+1, ermesg) - - do i = 1, min(np_add, np_remove) - j = indices_to_remove(i) - self%ids(j) = ids_to_add(i) - self%positions(:,j) = positions_to_add(:,i) - enddo - - if(n_diff > 0) then - ! all the particles to remove were removed and replaced. Just need to append - ! remaining particles to end of list - self%ids( self%np+1:self%np+n_diff) = ids_to_add( np_remove+1:np_add) - self%positions(:, self%np+1:self%np+n_diff) = positions_to_add(:,np_remove+1:np_add) - - self%np = self%np + n_diff - - else if(n_diff < 0) then - ! all the particles were added by filling in holes left by particles that - ! were previously removed. Now remove remaining particles, starting from the end, - ! by replacing the missing particle with a copy from the end. - - ! sort remaining indices in ascending order - call qksrt_quicksort(size(indices_to_remove), indices_to_remove, np_add+1, np_remove) - - do i = np_remove, np_add+1, -1 - if(self%np <= 0) exit - j = indices_to_remove(i) - self%ids ( j) = self%ids ( self%np) - self%positions(:,j) = self%positions(:,self%np) - self%np = self%np - 1 - enddo - endif - - if(ier/=0) ermesg = 'drifters::ERROR in drifters_core_remove_and_add' - end subroutine drifters_core_remove_and_add - -!############################################################################### - subroutine drifters_core_print(self1, ermesg1) - type(drifters_core_type) :: self1 - character(*), intent(out) :: ermesg1 - integer j - ermesg1 = '' - - print '(a,i10,a,i6,a,i6,a,i4,a,i4,a,i4)','it=',self1%it, & - & ' np=', self1%np, ' npdim=', self1%npdim - - print *,'ids and positions:' - do j = 1, self1%np - print *,self1%ids(j), self1%positions(:,j) - enddo - - end subroutine drifters_core_print - - -end module drifters_core_mod -!############################################################################### -!> @} -! close documentation grouping diff --git a/drifters/include/drifters_input.inc b/drifters/include/drifters_input.inc deleted file mode 100644 index 0327f67053..0000000000 --- a/drifters/include/drifters_input.inc +++ /dev/null @@ -1,450 +0,0 @@ -!*********************************************************************** -!* GNU Lesser General Public License -!* -!* This file is part of the GFDL Flexible Modeling System (FMS). -!* -!* FMS is free software: you can redistribute it and/or modify it under -!* the terms of the GNU Lesser General Public License as published by -!* the Free Software Foundation, either version 3 of the License, or (at -!* your option) any later version. -!* -!* FMS is distributed in the hope that it will be useful, but WITHOUT -!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -!* for more details. -!* -!* You should have received a copy of the GNU Lesser General Public -!* License along with FMS. If not, see . -!*********************************************************************** -!> @defgroup drifters_input_mod drifters_input_mod -!> @ingroup drifters -!> @brief Imports initial drifter positions from a netCDF file - -!> @addtogroup drifters_input_mod -!> @{ -module drifters_input_mod - implicit none - private - - public :: drifters_input_type, drifters_input_new, drifters_input_del, drifters_input_save, assignment(=) - - ! Globals - integer, parameter, private :: MAX_STR_LEN = 128 - ! Include variable "version" to be written to log file. -#include - character, parameter, private :: SEPARATOR = ' ' - !> @} - - !> @brief Input data type for drifters. - !! - !> @note Be sure to update drifters_input_new, drifters_input_del and drifters_input_copy_new - !! when adding members - !> @ingroup drifters_input_mod - type drifters_input_type - ! Be sure to update drifters_input_new, drifters_input_del and drifters_input_copy_new - ! when adding members - character(len=MAX_STR_LEN), allocatable :: position_names(:) - character(len=MAX_STR_LEN), allocatable :: position_units(:) - character(len=MAX_STR_LEN), allocatable :: field_names(:) - character(len=MAX_STR_LEN), allocatable :: field_units(:) - character(len=MAX_STR_LEN), allocatable :: velocity_names(:) - real , allocatable :: positions(:,:) - integer , allocatable :: ids(:) - character(len=MAX_STR_LEN) :: time_units - character(len=MAX_STR_LEN) :: title - character(len=MAX_STR_LEN) :: version - end type drifters_input_type - - !> @brief Assignment override for @ref drifters_input_type - !> @ingroup drifters_input_mod - interface assignment(=) - module procedure drifters_input_copy_new - end interface - -!> @addtogroup drifters_input_mod -!> @{ - - contains - -!=============================================================================== - - subroutine drifters_input_new(self, filename, ermesg) - use netcdf - use netcdf_nf_data - use netcdf_nf_interfaces - type(drifters_input_type) :: self - character(len=*), intent(in) :: filename - character(len=*), intent(out):: ermesg - - ! Local - integer :: ier, ncid, nd, nf, np, ipos, j, id, i, isz - character(len=MAX_STR_LEN) :: attribute - - ermesg = '' - - ier = nf_open(filename, NF_NOWRITE, ncid) - if(ier/=NF_NOERR) then - ermesg = 'drifters_input: ERROR could not open netcdf file '//filename - return - endif - - ! version - ier = NF_PUT_ATT_TEXT(NCID, NF_GLOBAL, 'version', len(version), version) - - ier = NF_INQ_DIMID(NCID, 'nd', id) - if(ier/=NF_NOERR) then - ermesg = 'drifters_input: ERROR could not find "nd" (number of dimensions)' - ier = nf_close(ncid) - return - endif - ier = NF_INQ_DIMLEN(NCID, id, nd) - - ! determine number of fields (nf) - attribute = '' - ier = nf_get_att_text(ncid, NF_GLOBAL, 'field_names', attribute) - isz = min(len(attribute), len(trim(attribute))+1) - attribute(isz:isz) = ' ' - ipos = 1 - nf = 0 - do i = 1, isz - if(attribute(i:i)==SEPARATOR) then - nf = nf + 1 - endif - enddo - - ier = NF_INQ_DIMID(NCID, 'np', id) - if(ier/=NF_NOERR) then - ermesg = 'drifters_input: ERROR could not find "np" (number of particles)' - ier = nf_close(ncid) - return - endif - ier = NF_INQ_DIMLEN(NCID, id, np) - - allocate(self%position_names(nd)) - allocate(self%position_units(nd)) - allocate(self%field_names(nf)) - allocate(self%field_units(nf)) - allocate(self%velocity_names(nd)) - allocate(self%ids(np)) - allocate(self%positions(nd, np)) - - ier = NF_INQ_VARID(NCID, 'ids', id) - if(ier/=NF_NOERR) then - ermesg = 'drifters_input: ERROR could not find "ids"' - ier = nf_close(ncid) - return - endif - ier = NF_GET_VAR_INT(NCID, id, self%ids) - - ier = NF_INQ_VARID(NCID, 'positions', id) - if(ier/=NF_NOERR) then - ermesg = 'drifters_input: ERROR could not find "positions"' - ier = nf_close(ncid) - return - endif - ier = NF90_GET_VAR(NCID, id, self%positions) - - attribute = '' - ier = nf_get_att_text(ncid, NF_GLOBAL, 'version', attribute) - self%version = trim(attribute) - - attribute = '' - ier = nf_get_att_text(ncid, NF_GLOBAL, 'time_units', attribute) - self%time_units = trim(attribute) - - attribute = '' - ier = nf_get_att_text(ncid, NF_GLOBAL, 'title', attribute) - self%title = trim(attribute) - - attribute = '' - ier = nf_get_att_text(ncid, id, 'names', attribute) - isz = min(len(attribute), len(trim(attribute))+1) - attribute(isz:isz) = ' ' - ipos = 1 - j = 1 - do i = 1, isz - if(attribute(i:i)==SEPARATOR) then - self%position_names(j) = trim(adjustl(attribute(ipos:i-1))) - ipos = i+1 - j = j + 1 - if(j > nd) exit - endif - enddo - - attribute = '' - ier = nf_get_att_text(ncid, id, 'units', attribute) - isz = min(len(attribute), len(trim(attribute))+1) - attribute(isz:isz) = ' ' - ipos = 1 - j = 1 - do i = 1, isz - if(attribute(i:i)==SEPARATOR) then - self%position_units(j) = trim(adjustl(attribute(ipos:i-1))) - ipos = i+1 - j = j + 1 - if(j > nd) exit - endif - enddo - - attribute = '' - ier = nf_get_att_text(ncid, NF_GLOBAL, 'field_names', attribute) - isz = min(len(attribute), len(trim(attribute))+1) - attribute(isz:isz) = ' ' - ipos = 1 - j = 1 - do i = 1, isz - if(attribute(i:i)==SEPARATOR) then - self%field_names(j) = trim(adjustl(attribute(ipos:i-1))) - ipos = i+1 - j = j + 1 - if(j > nf) exit - endif - enddo - - attribute = '' - ier = nf_get_att_text(ncid, NF_GLOBAL, 'field_units', attribute) - isz = min(len(attribute), len(trim(attribute))+1) - attribute(isz:isz) = ' ' - ipos = 1 - j = 1 - do i = 1, isz - if(attribute(i:i)==SEPARATOR) then - self%field_units(j) = trim(adjustl(attribute(ipos:i-1))) - ipos = i+1 - j = j + 1 - if(j > nf) exit - endif - enddo - - attribute = '' - ier = nf_get_att_text(ncid, NF_GLOBAL, 'velocity_names', attribute) - isz = min(len(attribute), len(trim(attribute))+1) - attribute(isz:isz) = ' ' - ipos = 1 - j = 1 - do i = 1, isz - if(attribute(i:i)==SEPARATOR) then - self%velocity_names(j) = trim(adjustl(attribute(ipos:i-1))) - ipos = i+1 - j = j + 1 - if(j > nd) exit - endif - enddo - - end subroutine drifters_input_new - -!=============================================================================== - subroutine drifters_input_del(self, ermesg) - type(drifters_input_type) :: self - character(len=*), intent(out):: ermesg - - integer :: iflag - - ermesg = '' - - deallocate(self%position_names, stat=iflag) - deallocate(self%position_units, stat=iflag) - deallocate(self%field_names, stat=iflag) - deallocate(self%field_units, stat=iflag) - deallocate(self%velocity_names, stat=iflag) - deallocate(self%ids, stat=iflag) - deallocate(self%positions, stat=iflag) - - end subroutine drifters_input_del - -!=============================================================================== - subroutine drifters_input_copy_new(new_instance, old_instance) - - type(drifters_input_type), intent(inout) :: new_instance - type(drifters_input_type), intent(in) :: old_instance - - allocate(new_instance%position_names( size(old_instance%position_names) )) - allocate(new_instance%position_units( size(old_instance%position_units) )) - allocate(new_instance%field_names( size(old_instance%field_names) )) - allocate(new_instance%field_units( size(old_instance%field_units) )) - allocate(new_instance%velocity_names( size(old_instance%velocity_names) )) - new_instance%position_names = old_instance%position_names - new_instance%position_units = old_instance%position_units - new_instance%field_names = old_instance%field_names - new_instance%field_units = old_instance%field_units - new_instance%velocity_names = old_instance%velocity_names - new_instance%time_units = old_instance%time_units - new_instance%title = old_instance%title - new_instance%version = old_instance%version - allocate(new_instance%positions( size(old_instance%positions,1),size(old_instance%positions,2) )) - new_instance%positions = old_instance%positions - allocate(new_instance%ids(size(old_instance%ids))) - new_instance%ids = old_instance%ids - - end subroutine drifters_input_copy_new - -!=============================================================================== - !> @brief save state in netcdf file. can be used as restart file. - subroutine drifters_input_save(self, filename, geolon, geolat, ermesg) - ! save state in netcdf file. can be used as restart file. - use netcdf - use netcdf_nf_data - use netcdf_nf_interfaces - type(drifters_input_type) :: self - character(len=*), intent(in ):: filename - real, intent(in), optional :: geolon(:), geolat(:) - character(len=*), intent(out):: ermesg - - - integer ncid, nc_nd, nc_np, ier, nd, np, nf, nc_pos, nc_ids, i, j, n - integer nc_lon, nc_lat - character(len=MAX_STR_LEN) :: att - - - ermesg = '' - - ier = nf_create(filename, NF_CLOBBER, ncid) - if(ier/=NF_NOERR) then - ermesg = 'drifters_input: ERROR cannot create '//filename - return - endif - - nd = size(self%positions, 1) - np = size(self%positions, 2) - nf = size(self%field_names) - - ! dimensions - ier = nf_def_dim(ncid, 'nd', nd, nc_nd) - if(ier/=NF_NOERR) ermesg = 'drifters_input_save: ERROR creating dim "nd" '//nf_strerror(ier) - - ier = nf_def_dim(ncid, 'np', np, nc_np) - if(ier/=NF_NOERR) ermesg = 'drifters_input_save: ERROR creating dim "np" '//nf_strerror(ier) - - ! global attributes - ier = nf_put_att_text(ncid, NF_GLOBAL, 'title', len_trim(self%title), self%title) - if(ier/=NF_NOERR) ermesg = 'drifters_input_save: ERROR setting global att "title" ' & - & //nf_strerror(ier) - - ier = nf_put_att_text(ncid, NF_GLOBAL, 'time_units', len_trim(self%time_units), self%time_units) - if(ier/=NF_NOERR) ermesg = 'drifters_input_save: ERROR setting global att "time_units" ' & - & //nf_strerror(ier) - - att = '' - j = 1 - do i = 1, nf - n = len_trim(self%field_units(i)) - att(j:j+n+1) = trim(self%field_units(i)) // ' ' - j = j + n + 1 - enddo - ier = nf_put_att_text(ncid, NF_GLOBAL, 'field_units', len_trim(att), & - & att) - if(ier/=NF_NOERR) ermesg = 'drifters_input_save: ERROR setting global att "field_units" ' & - & //nf_strerror(ier) - - att = '' - j = 1 - do i = 1, nf - n = len_trim(self%field_names(i)) - att(j:j+n+1) = trim(self%field_names(i)) // ' ' - j = j + n + 1 - enddo - ier = nf_put_att_text(ncid, NF_GLOBAL, 'field_names', len_trim(att), & - & att) - if(ier/=NF_NOERR) ermesg = 'drifters_input_save: ERROR setting global att "field_names" ' & - & //nf_strerror(ier) - - att = '' - j = 1 - do i = 1, nd - n = len_trim(self%velocity_names(i)) - att(j:j+n+1) = trim(self%velocity_names(i)) // ' ' - j = j + n + 1 - enddo - ier = nf_put_att_text(ncid, NF_GLOBAL, 'velocity_names', len_trim(att), & - & att) - if(ier/=NF_NOERR) ermesg = 'drifters_input_save: ERROR setting global att "velocity_names" ' & - & //nf_strerror(ier) - - ! variables - ier = nf_def_var(ncid, 'positions', NF_DOUBLE, 2, (/nc_nd, nc_np/), nc_pos) - if(ier/=NF_NOERR) ermesg = 'drifters_input_save: ERROR creating var "positions" '//nf_strerror(ier) - - ier = nf_def_var(ncid, 'ids', NF_INT, 1, (/nc_np/), nc_ids) - if(ier/=NF_NOERR) ermesg = 'drifters_input_save: ERROR creating var "ids" '//nf_strerror(ier) - - ! optional: longitudes/latitudes in deg - if(present(geolon)) then - ier = nf_def_var(ncid, 'longitude', NF_DOUBLE, 1, (/nc_np/), nc_lon) - if(ier/=NF_NOERR) ermesg = 'drifters_input_save: ERROR creating var "longitude" ' & - & //nf_strerror(ier) - att = 'degrees_east' - ier = nf_put_att_text(ncid, nc_lon, 'units', len(trim(att)), trim(att)) - if(ier/=NF_NOERR) ermesg = 'drifters_input_save: ERROR setting att "units" to "longitude" ' & - & //nf_strerror(ier) - endif - if(present(geolat)) then - ier = nf_def_var(ncid, 'latitude', NF_DOUBLE, 1, (/nc_np/), nc_lat) - if(ier/=NF_NOERR) ermesg = 'drifters_input_save: ERROR creating var "latitude" ' & - & //nf_strerror(ier) - att = 'degrees_north' - ier = nf_put_att_text(ncid, nc_lat, 'units', len(trim(att)), trim(att)) - if(ier/=NF_NOERR) ermesg = 'drifters_input_save: ERROR setting att "units" to "latitude" ' & - & //nf_strerror(ier) - endif - - ! variable attributes - - att = '' - j = 1 - do i = 1, nd - n = len_trim(self%position_units(i)) - att(j:j+n+1) = trim(self%position_units(i)) // ' ' - j = j + n + 1 - enddo - ier = nf_put_att_text(ncid, nc_pos, 'units', len_trim(att), & - & att) - if(ier/=NF_NOERR) ermesg = 'drifters_input_save: ERROR setting att "units" to "positions" ' & - & //nf_strerror(ier) - - att = '' - j = 1 - do i = 1, nd - n = len_trim(self%position_names(i)) - att(j:j+n+1) = trim(self%position_names(i)) // ' ' - j = j + n + 1 - enddo - ier = nf_put_att_text(ncid, nc_pos, 'names', len_trim(att), & - & att) - if(ier/=NF_NOERR) ermesg = 'drifters_input_save: ERROR setting att "names" to "positions" ' & - & //nf_strerror(ier) - - ! end of define mode - ier = nf_enddef(ncid) - if(ier/=NF_NOERR) ermesg = 'drifters_input_save: ERROR could not end define mode ' & - & //nf_strerror(ier) - - ! data - ier = nf90_put_var(ncid, nc_pos, self%positions) - if(ier/=NF_NOERR) ermesg = 'drifters_input_save: ERROR could not write "positions" ' & - & //nf_strerror(ier) - - ier = nf90_put_var(ncid, nc_ids, self%ids) - if(ier/=NF_NOERR) ermesg = 'drifters_input_save: ERROR could not write "ids" ' & - & //nf_strerror(ier) - - if(present(geolon)) then - ier = nf90_put_var(ncid, nc_lon, geolon) - if(ier/=NF_NOERR) ermesg = 'drifters_input_save: ERROR could not write "geolon" ' & - & //nf_strerror(ier) - endif - if(present(geolat)) then - ier = nf90_put_var(ncid, nc_lat, geolat) - if(ier/=NF_NOERR) ermesg = 'drifters_input_save: ERROR could not write "geolat" ' & - & //nf_strerror(ier) - endif - - - ier = nf_close(ncid) - if(ier/=NF_NOERR) ermesg = 'drifters_input_save: ERROR could not close file ' & - & //nf_strerror(ier) - - end subroutine drifters_input_save - -end module drifters_input_mod -!> @} -! close documentation grouping diff --git a/drifters/include/drifters_io.inc b/drifters/include/drifters_io.inc deleted file mode 100644 index 3592da9603..0000000000 --- a/drifters/include/drifters_io.inc +++ /dev/null @@ -1,313 +0,0 @@ -!*********************************************************************** -!* GNU Lesser General Public License -!* -!* This file is part of the GFDL Flexible Modeling System (FMS). -!* -!* FMS is free software: you can redistribute it and/or modify it under -!* the terms of the GNU Lesser General Public License as published by -!* the Free Software Foundation, either version 3 of the License, or (at -!* your option) any later version. -!* -!* FMS is distributed in the hope that it will be useful, but WITHOUT -!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -!* for more details. -!* -!* You should have received a copy of the GNU Lesser General Public -!* License along with FMS. If not, see . -!*********************************************************************** -!> @defgroup drifters_io_mod drifters_io_mod -!> @ingroup drifters -!> @brief Saves drifter data for postprocessing and restarts - -!> @addtogroup drifters_io_mod -!> @{ -module drifters_io_mod - - use netcdf - use netcdf_nf_data - use netcdf_nf_interfaces - use netcdf4_nf_interfaces - - implicit none - private - - public :: drifters_io_type, drifters_io_new, drifters_io_del, drifters_io_set_time_units - public :: drifters_io_set_position_names, drifters_io_set_position_units, drifters_io_set_field_names - public :: drifters_io_set_field_units, drifters_io_write - - ! Globals - integer, parameter, private :: MAX_STR_LEN = 128 - ! Include variable "version" to be written to log file. -#include - - real :: drfts_eps_t = 10.*epsilon(1.) - -!> @} - !> @brief IO data for drifters. - !> @ingroup drifters_input_mod - type drifters_io_type - real :: time - integer :: it !< time index - integer :: it_id !< infinite axis index - integer :: ncid - integer :: nc_positions, nc_fields, nc_ids, nc_time, nc_index_time - logical :: enddef - end type drifters_io_type -!> @addtogroup drifters_io_mod -!> @{ -contains - -!############################################################################### - subroutine drifters_io_new(self, filename, nd, nf, ermesg) - type(drifters_io_type) :: self - character(len=*), intent(in) :: filename - integer, intent(in) :: nd !< number of dims - integer, intent(in) :: nf !< number of fields - character(len=*), intent(out) :: ermesg - - integer ier, nc_it_id, nc_nd, nc_nf - integer :: size1(1), size2(2) - - ermesg='' - self%enddef = .FALSE. - - ier = nf_create(filename, NF_CLOBBER, self%ncid) - if(ier/=NF_NOERR) ermesg = 'drifters_io_new::nf_create ('//filename//') '//nf_strerror(ier) - - ! global attributes - ier = nf_put_att_text(self%ncid, NF_GLOBAL, 'version', len_trim(version), trim(version)) - - - ! dimensions - ier = nf_def_dim(self%ncid, 'np', NF_UNLIMITED, nc_it_id) - if(ier/=NF_NOERR) ermesg = 'drifters_io_new::nf_def_dim (it_id) '//nf_strerror(ier) - - ier = nf_def_dim(self%ncid, 'nf', nf, nc_nf) - if(ier/=NF_NOERR) ermesg = 'drifters_io_new::nf_def_dim (nf) '//nf_strerror(ier) - - ier = nf_def_dim(self%ncid, 'nd', nd, nc_nd) - if(ier/=NF_NOERR) ermesg = 'drifters_io_new::nf_def_dim (nd) '//nf_strerror(ier) - - ! variables - size1 = (/nc_it_id/) - ier = nf_def_var(self%ncid, 'index_time', NF_INT, 1, size1, self%nc_index_time) - if(ier/=NF_NOERR) ermesg = 'drifters_io_new::nf_def_var (index_time)'//nf_strerror(ier) - - ier = nf_def_var(self%ncid, 'time', NF_DOUBLE, 1, size1, self%nc_time) - if(ier/=NF_NOERR) ermesg = 'drifters_io_new::nf_def_var (time)'//nf_strerror(ier) - - ier = nf_def_var(self%ncid, 'ids', NF_INT, 1, size1, self%nc_ids) - if(ier/=NF_NOERR) ermesg = 'drifters_io_new::nf_def_var (ids)'//nf_strerror(ier) - - size2 = (/nc_nd, nc_it_id/) - ier = nf_def_var(self%ncid, 'positions', NF_DOUBLE, 2, size2, self%nc_positions) - if(ier/=NF_NOERR) ermesg = 'drifters_io_new::nf_def_var (positions)'//nf_strerror(ier) - - size2 = (/nc_nf, nc_it_id/) - ier = nf_def_var(self%ncid, 'fields', NF_DOUBLE, 2, size2, self%nc_fields) - if(ier/=NF_NOERR) ermesg = 'drifters_io_new::nf_def_var (fields)'//nf_strerror(ier) - - self%time = -huge(1.) - self%it = -1 - self%it_id = 1 - - end subroutine drifters_io_new - -!############################################################################### - subroutine drifters_io_del(self, ermesg) - type(drifters_io_type) :: self - character(len=*), intent(out) :: ermesg - - integer ier - - ermesg = '' - - ier = nf_close(self%ncid) - if(ier/=NF_NOERR) ermesg = 'drifters_io_del::nf_close '//nf_strerror(ier) - - end subroutine drifters_io_del - -!############################################################################### - subroutine drifters_io_set_time_units(self, name, ermesg) - type(drifters_io_type) :: self - character(len=*), intent(in) :: name - character(len=*), intent(out) :: ermesg - - integer ier - - ermesg = '' - ier = nf_put_att_text(self%ncid, NF_GLOBAL, & - & 'time_units', len_trim(name), trim(name)) - if(ier/=NF_NOERR) & - & ermesg = 'drifters_io_set_time_units::failed to add time_units attribute ' & - & //nf_strerror(ier) - - end subroutine drifters_io_set_time_units - -!############################################################################### - subroutine drifters_io_set_position_names(self, names, ermesg) - type(drifters_io_type) :: self - character(len=*), intent(in) :: names(:) - character(len=*), intent(out) :: ermesg - - integer n, ier, i - character(len=128) :: attname - - n = size(names) - ermesg = '' - - do i = 1, n - write(attname, '(i6)' ) i - attname = 'name_'//adjustl(attname) - ier = nf_put_att_text(self%ncid, self%nc_positions, & - & trim(attname), len_trim(names(i)), trim(names(i))) - if(ier/=NF_NOERR) & - & ermesg = 'drifters_io_set_position_names::failed to add name attribute to positions '//nf_strerror(ier) - enddo - - end subroutine drifters_io_set_position_names - -!############################################################################### - subroutine drifters_io_set_position_units(self, names, ermesg) - type(drifters_io_type) :: self - character(len=*), intent(in) :: names(:) - character(len=*), intent(out) :: ermesg - - integer n, ier, i - character(len=128) :: attname - - n = size(names) - ermesg = '' - - do i = 1, n - write(attname, '(i6)' ) i - attname = 'unit_'//adjustl(attname) - ier = nf_put_att_text(self%ncid, self%nc_positions, & - & trim(attname), len_trim(names(i)), trim(names(i))) - if(ier/=NF_NOERR) & - & ermesg = 'drifters_io_set_position_names::failed to add unit attribute to positions '//nf_strerror(ier) - enddo - - end subroutine drifters_io_set_position_units - -!############################################################################### - subroutine drifters_io_set_field_names(self, names, ermesg) - type(drifters_io_type) :: self - character(len=*), intent(in) :: names(:) - character(len=*), intent(out) :: ermesg - - integer n, ier, i - character(len=128) :: attname - - n = size(names) - ermesg = '' - - do i = 1, n - write(attname, '(i6)' ) i - attname = 'name_'//adjustl(attname) - ier = nf_put_att_text(self%ncid, self%nc_fields, & - & trim(attname), len_trim(names(i)), trim(names(i))) - if(ier/=NF_NOERR) & - & ermesg = 'drifters_io_set_field_names::failed to add name attribute to fields '//nf_strerror(ier) - enddo - - end subroutine drifters_io_set_field_names - -!############################################################################### - subroutine drifters_io_set_field_units(self, names, ermesg) - type(drifters_io_type) :: self - character(len=*), intent(in) :: names(:) - character(len=*), intent(out) :: ermesg - - integer n, ier, i - character(len=128) :: attname - - n = size(names) - ermesg = '' - - do i = 1, n - write(attname, '(i6)' ) i - attname = 'unit_'//adjustl(attname) - ier = nf_put_att_text(self%ncid, self%nc_fields, & - & trim(attname), len_trim(names(i)), trim(names(i))) - if(ier/=NF_NOERR) & - & ermesg = 'drifters_io_set_field_units::failed to add unit attribute to fields '//nf_strerror(ier) - enddo - - end subroutine drifters_io_set_field_units -!############################################################################### - - subroutine drifters_io_write(self, time, np, nd, nf, ids, positions, fields, ermesg) - type(drifters_io_type) :: self - real, intent(in) :: time - integer, intent(in) :: np !< number of dirfters - integer, intent(in) :: nd !< number of dimensions - integer, intent(in) :: nf !< number of fields - integer, intent(in) :: ids(np) !< of size np - real, intent(in) :: positions(nd,np) !< nd times np - real, intent(in) :: fields(nf,np) !< nf times np - character(len=*), intent(out) :: ermesg - - integer ier, i - integer :: start1(1), len1(1), start2(2), len2(2) - integer :: it_indices(np) - real :: time_array(np) - - ermesg = '' - - if(.not. self%enddef) then - ier = nf_enddef(self%ncid) - if(ier/=NF_NOERR) then - ermesg = 'drifters_io_write::nf_enddef failure. No data will be written. '//nf_strerror(ier) - return - endif - self%enddef = .TRUE. - endif - - if(abs(time - self%time) > drfts_eps_t) then - self%it = self%it + 1 - self%time = time - endif - - start1(1) = self%it_id - len1(1) = np - - it_indices = (/(self%it,i=1,np)/) - ier = nf_put_vara_int( self%ncid, self%nc_index_time, start1, len1, it_indices ) - if(ier/=NF_NOERR) & - & ermesg = 'drifters_io_write::failed to write index_time: ' //nf_strerror(ier) - - time_array = (/(time,i=1,np)/) - ier = nf90_put_var( self%ncid, self%nc_time, time_array, start1, len1 ) - if(ier/=NF_NOERR) & - & ermesg = 'drifters_io_write::failed to write time: ' //nf_strerror(ier) - - ier = nf_put_vara_int(self%ncid, self%nc_ids, start1, len1, ids) - if(ier/=NF_NOERR) & - & ermesg = 'drifters_io_write::failed to write ids: '//nf_strerror(ier) - - start2(1) = 1 - start2(2) = self%it_id - - len2(1) = nd - len2(2) = np - - ier = nf90_put_var(self%ncid, self%nc_positions, positions, start2, len2) - if(ier/=NF_NOERR) & - & ermesg = 'drifters_io_write::failed to write positions: '//nf_strerror(ier) - - len2(1) = nf - len2(2) = np - - ier = nf90_put_var(self%ncid, self%nc_fields, fields, start2, len2) - if(ier/=NF_NOERR) & - & ermesg = 'drifters_io_write::failed to write fields: '//nf_strerror(ier) - - self%it_id = self%it_id + np - - end subroutine drifters_io_write - -end module drifters_io_mod -!> @} -! close documentation grouping diff --git a/drifters/include/quicksort.inc b/drifters/include/quicksort.inc deleted file mode 100644 index 6f751858d1..0000000000 --- a/drifters/include/quicksort.inc +++ /dev/null @@ -1,94 +0,0 @@ -!*********************************************************************** -!* GNU Lesser General Public License -!* -!* This file is part of the GFDL Flexible Modeling System (FMS). -!* -!* FMS is free software: you can redistribute it and/or modify it under -!* the terms of the GNU Lesser General Public License as published by -!* the Free Software Foundation, either version 3 of the License, or (at -!* your option) any later version. -!* -!* FMS is distributed in the hope that it will be useful, but WITHOUT -!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -!* for more details. -!* -!* You should have received a copy of the GNU Lesser General Public -!* License along with FMS. If not, see . -!*********************************************************************** -!> @cond -#undef _TYP -#define _TYP integer -!> @endcond - -!> @defgroup quicksort quicksort -!> @ingroup drifters -!> @brief Fortran implementation of quicksort to be used in @ref drifters_core -!! -!> @author Magnus Lie Hetland - -!> Create array partitions for quicksort -function qksrt_partition(n, list, start, end) result(top) - implicit none - integer, intent(in) :: n - _TYP, intent(inout) :: list(n) - integer, intent(in) :: start, end - - integer pivot, bottom, top - logical done - - pivot = list(end) ! Partition around the last value - bottom = start-1 ! Start outside the area to be partitioned - top = end ! Ditto - - done = .false. - do while (.not. done) ! Until all elements are partitioned... - - do while (.not. done) ! Until we find an out of place element... - bottom = bottom+1 ! ... move the bottom up. - - if(bottom == top) then ! If we hit the top... - done = .true. ! ... we are done. - exit - endif - - if(list(bottom) > pivot) then ! Is the bottom out of place? - list(top) = list(bottom) ! Then put it at the top... - exit ! ... and start searching from the top. - endif - enddo - - do while (.not. done) ! Until we find an out of place element... - top = top-1 ! ... move the top down. - - if(top == bottom) then ! If we hit the bottom... - done = .true. ! ... we are done. - exit - endif - - if(list(top) < pivot) then ! Is the top out of place? - list(bottom) = list(top) ! Then put it at the bottom... - exit ! ...and start searching from the bottom. - endif - enddo - enddo - - list(top) = pivot ! Put the pivot in its place. - ! Return the split point - -end function qksrt_partition - -!> quicksort a given list -recursive subroutine qksrt_quicksort(n, list, start, end) - implicit none - integer, intent(in) :: n - _TYP, intent(inout) :: list(n) - integer, intent(in) :: start, end - integer :: split, qksrt_partition - external :: qksrt_partition - if(start < end) then ! If there are two or more elements... - split = qksrt_partition(n, list, start, end) ! ... partition the sublist... - call qksrt_quicksort(n, list, start, split-1) ! ... and sort both halves. - call qksrt_quicksort(n, list, split+1, end) - endif -end subroutine qksrt_quicksort diff --git a/fms2_io/include/compressed_write.inc b/fms2_io/include/compressed_write.inc index cd2919c162..e284a0892a 100644 --- a/fms2_io/include/compressed_write.inc +++ b/fms2_io/include/compressed_write.inc @@ -144,22 +144,22 @@ subroutine compressed_write_1d(fileobj, variable_name, cdata, unlim_dim_level, & type is (integer(kind=i4_kind)) call mpp_gather(cdata, size(cdata), buf_i4_kind, fileobj%compressed_dims(compressed_dim_index(2))%npes_nelems, & fileobj%pelist) - call netcdf_write_data(fileobj, variable_name, buf_i4_kind, & + if (fileobj%is_root) call netcdf_write_data(fileobj, variable_name, buf_i4_kind, & unlim_dim_level=unlim_dim_level) type is (integer(kind=i8_kind)) call mpp_gather(cdata, size(cdata), buf_i8_kind, fileobj%compressed_dims(compressed_dim_index(2))%npes_nelems, & fileobj%pelist) - call netcdf_write_data(fileobj, variable_name, buf_i8_kind, & + if (fileobj%is_root) call netcdf_write_data(fileobj, variable_name, buf_i8_kind, & unlim_dim_level=unlim_dim_level) type is (real(kind=r4_kind)) call mpp_gather(cdata, size(cdata), buf_r4_kind, fileobj%compressed_dims(compressed_dim_index(2))%npes_nelems, & fileobj%pelist) - call netcdf_write_data(fileobj, variable_name, buf_r4_kind, & + if (fileobj%is_root) call netcdf_write_data(fileobj, variable_name, buf_r4_kind, & unlim_dim_level=unlim_dim_level) type is (real(kind=r8_kind)) call mpp_gather(cdata, size(cdata), buf_r8_kind, fileobj%compressed_dims(compressed_dim_index(2))%npes_nelems, & fileobj%pelist) - call netcdf_write_data(fileobj, variable_name, buf_r8_kind, & + if (fileobj%is_root) call netcdf_write_data(fileobj, variable_name, buf_r8_kind, & unlim_dim_level=unlim_dim_level) class default call error("unsupported variable type: "//trim(append_error_msg)) @@ -266,19 +266,19 @@ subroutine compressed_write_2d(fileobj, variable_name, cdata, unlim_dim_level, & select type(cdata) type is (integer(kind=i4_kind)) call mpp_gather(is, ie, js, je, fileobj%pelist, cdata, buf_i4_kind, fileobj%is_root) - call netcdf_write_data(fileobj, variable_name, buf_i4_kind, & + if (fileobj%is_root) call netcdf_write_data(fileobj, variable_name, buf_i4_kind, & unlim_dim_level=unlim_dim_level) type is (integer(kind=i8_kind)) call mpp_gather(is, ie, js, je, fileobj%pelist, cdata, buf_i8_kind, fileobj%is_root) - call netcdf_write_data(fileobj, variable_name, buf_i8_kind, & + if (fileobj%is_root) call netcdf_write_data(fileobj, variable_name, buf_i8_kind, & unlim_dim_level=unlim_dim_level) type is (real(kind=r4_kind)) call mpp_gather(is, ie, js, je, fileobj%pelist, cdata, buf_r4_kind, fileobj%is_root) - call netcdf_write_data(fileobj, variable_name, buf_r4_kind, & + if (fileobj%is_root) call netcdf_write_data(fileobj, variable_name, buf_r4_kind, & unlim_dim_level=unlim_dim_level) type is (real(kind=r8_kind)) call mpp_gather(is, ie, js, je, fileobj%pelist, cdata, buf_r8_kind, fileobj%is_root) - call netcdf_write_data(fileobj, variable_name, buf_r8_kind, & + if (fileobj%is_root) call netcdf_write_data(fileobj, variable_name, buf_r8_kind, & unlim_dim_level=unlim_dim_level) class default call error("unsupported variable type: "//trim(append_error_msg)) @@ -391,22 +391,22 @@ subroutine compressed_write_3d(fileobj, variable_name, cdata, unlim_dim_level, & type is (integer(kind=i4_kind)) call mpp_gather(is, ie, js, je, size(cdata,3), & fileobj%pelist, cdata, buf_i4_kind, fileobj%is_root) - call netcdf_write_data(fileobj, variable_name, buf_i4_kind, & + if (fileobj%is_root) call netcdf_write_data(fileobj, variable_name, buf_i4_kind, & unlim_dim_level=unlim_dim_level) type is (integer(kind=i8_kind)) call mpp_gather(is, ie, js, je, size(cdata,3), & fileobj%pelist, cdata, buf_i8_kind, fileobj%is_root) - call netcdf_write_data(fileobj, variable_name, buf_i8_kind, & + if (fileobj%is_root) call netcdf_write_data(fileobj, variable_name, buf_i8_kind, & unlim_dim_level=unlim_dim_level) type is (real(kind=r4_kind)) call mpp_gather(is, ie, js, je, size(cdata,3), & fileobj%pelist, cdata, buf_r4_kind, fileobj%is_root) - call netcdf_write_data(fileobj, variable_name, buf_r4_kind, & + if (fileobj%is_root) call netcdf_write_data(fileobj, variable_name, buf_r4_kind, & unlim_dim_level=unlim_dim_level) type is (real(kind=r8_kind)) call mpp_gather(is, ie, js, je, size(cdata,3), & fileobj%pelist, cdata, buf_r8_kind, fileobj%is_root) - call netcdf_write_data(fileobj, variable_name, buf_r8_kind, & + if (fileobj%is_root) call netcdf_write_data(fileobj, variable_name, buf_r8_kind, & unlim_dim_level=unlim_dim_level) class default call error("unsupported variable type: "//trim(append_error_msg)) diff --git a/libFMS/Makefile.am b/libFMS/Makefile.am index e56820e701..db57f86562 100644 --- a/libFMS/Makefile.am +++ b/libFMS/Makefile.am @@ -28,7 +28,7 @@ lib_LTLIBRARIES = libFMS.la # These linker flags specify libtool version info. # See http://www.gnu.org/software/libtool/manual/libtool.html#Libtool-versioning # for information regarding incrementing `-version-info`. -libFMS_la_LDFLAGS = -version-info 15:0:0 +libFMS_la_LDFLAGS = -version-info 16:0:0 # Add the convenience libraries to the FMS library. libFMS_la_LIBADD = $(top_builddir)/platform/libplatform.la diff --git a/mpp/include/mpp_domains_misc.inc b/mpp/include/mpp_domains_misc.inc index 683ffd18a2..2c8de69237 100644 --- a/mpp/include/mpp_domains_misc.inc +++ b/mpp/include/mpp_domains_misc.inc @@ -419,6 +419,13 @@ end subroutine init_nonblock_type field2(isd:ied, jsd:jed) = field_in(:,:) endif +! allocate field1 on pelist1 +! if field1 is left unallocated, the code will try to access unallocated memory +! when field1 is set to 0 in mpp_redistribute + if(any(pelist1 == pe)) then + allocate(field1(1,1)) + endif + ! broadcast domain call mpp_broadcast_domain(domain1) call mpp_broadcast_domain(domain2) @@ -443,6 +450,8 @@ end subroutine init_nonblock_type if(any(pelist2 == pe)) deallocate(field1, field2) + if(any(pelist1 == pe)) deallocate(field1) + call mpp_sync() return diff --git a/mpp/include/mpp_gather.fh b/mpp/include/mpp_gather.fh index 17b09c0312..8ead643f3a 100644 --- a/mpp/include/mpp_gather.fh +++ b/mpp/include/mpp_gather.fh @@ -111,19 +111,22 @@ end subroutine MPP_GATHER_1DV_ subroutine MPP_GATHER_PELIST_2D_(is, ie, js, je, pelist, array_seg, data, is_root_pe, & ishift, jshift) - integer, intent(in) :: is, ie, js, je - integer, dimension(:), intent(in) :: pelist - MPP_TYPE_, dimension(is:ie,js:je), intent(in) :: array_seg - MPP_TYPE_, dimension(:,:), intent(inout) :: data - logical, intent(in) :: is_root_pe - integer, optional, intent(in) :: ishift, jshift - - MPP_TYPE_ :: arr3D(size(array_seg,1),size(array_seg,2),1) - MPP_TYPE_ :: data3D(size( data,1),size( data,2),1) - pointer( aptr, arr3D ) - pointer( dptr, data3D ) - aptr = LOC(array_seg) - dptr = LOC( data) + integer, intent(in) :: is, ie, js, je + integer, dimension(:), intent(in) :: pelist + MPP_TYPE_, dimension(is:ie,js:je), target, intent(in) :: array_seg + MPP_TYPE_, dimension(:,:), contiguous, target, intent(inout) :: data + logical, intent(in) :: is_root_pe + integer, optional, intent(in) :: ishift, jshift + + MPP_TYPE_, pointer :: arr3D(:,:,:) + MPP_TYPE_, pointer :: data3D(:,:,:) + + arr3D(1:size(array_seg,1),1:size(array_seg,2),1:1) => array_seg + if (is_root_pe) then + data3D(1:size(data,1),1:size(data,2),1:1) => data + else + data3D => null() + endif call mpp_gather(is, ie, js, je, 1, pelist, arr3D, data3D, is_root_pe, & ishift, jshift) diff --git a/mpp/include/mpp_scatter.fh b/mpp/include/mpp_scatter.fh index 4223f79c39..fce54f5a78 100644 --- a/mpp/include/mpp_scatter.fh +++ b/mpp/include/mpp_scatter.fh @@ -29,17 +29,20 @@ subroutine MPP_SCATTER_PELIST_2D_(is, ie, js, je, pelist, array_seg, data, is_ro integer, intent(in) :: is, ie, js, je !< indices of segment array integer, dimension(:), intent(in) :: pelist ! array_seg + if (is_root_pe) then + data3D(1:size(data,1),1:size(data,2),1:1) => data + else + data3D => null() + endif call mpp_scatter(is, ie, js, je, 1, pelist, arr3D, data3D, is_root_pe, & ishift, jshift) diff --git a/mpp/include/mpp_util_mpi.inc b/mpp/include/mpp_util_mpi.inc index ef9443783a..7d235be83b 100644 --- a/mpp/include/mpp_util_mpi.inc +++ b/mpp/include/mpp_util_mpi.inc @@ -93,7 +93,6 @@ function get_peset(pelist) integer, intent(in), optional :: pelist(:) integer :: errunit integer :: i, n - integer, allocatable :: sorted(:) if( .NOT.PRESENT(pelist) )then !set it to current_peset_num get_peset = current_peset_num; return @@ -106,17 +105,14 @@ function get_peset(pelist) enddo endif - allocate( sorted(size(pelist(:))) ) - sorted = pelist errunit = stderr() if( debug )write( errunit,* )'pelist=', pelist !find if this array matches any existing peset do i = 1,peset_num if( debug )write( errunit,'(a,3i6)' )'pe, i, peset_num=', pe, i, peset_num - if( size(sorted(:)).EQ.size(peset(i)%list(:)) )then - if( ALL(sorted.EQ.peset(i)%list) )then - deallocate(sorted) + if( size(pelist(:)).EQ.size(peset(i)%list(:)) )then + if( ALL(pelist.EQ.peset(i)%list) )then get_peset = i; return end if end if @@ -126,14 +122,13 @@ function get_peset(pelist) if( peset_num > current_peset_max ) call expand_peset() i = peset_num !shorthand !create list - allocate( peset(i)%list(size(sorted(:))) ) - peset(i)%list(:) = sorted(:) - peset(i)%count = size(sorted(:)) + allocate( peset(i)%list(size(pelist(:))) ) + peset(i)%list(:) = pelist(:) + peset(i)%count = size(pelist(:)) - call MPI_GROUP_INCL( peset(current_peset_num)%group, size(sorted(:)), sorted-mpp_root_pe(), peset(i)%group, error ) + call MPI_GROUP_INCL( peset(current_peset_num)%group, size(pelist(:)), pelist-mpp_root_pe(), peset(i)%group, error ) call MPI_COMM_CREATE_GROUP(peset(current_peset_num)%id, peset(i)%group, & DEFAULT_TAG, peset(i)%id, error ) - deallocate(sorted) get_peset = i return diff --git a/mpp/mpp_domains.F90 b/mpp/mpp_domains.F90 index b2ccf3540f..02db652bc3 100644 --- a/mpp/mpp_domains.F90 +++ b/mpp/mpp_domains.F90 @@ -452,7 +452,7 @@ module mpp_domains_mod type :: nest_domain_type character(len=NAME_LENGTH) :: name integer :: num_level - integer, pointer :: nest_level(:) !< Added for moving nest functionality + integer, pointer :: nest_level(:) => NULL() !< Added for moving nest functionality type(nest_level_type), pointer :: nest(:) => NULL() integer :: num_nest integer, pointer :: tile_fine(:), tile_coarse(:) diff --git a/test_fms/diag_manager/test_diag_manager2.sh b/test_fms/diag_manager/test_diag_manager2.sh index 747be8e691..a625db4d1e 100755 --- a/test_fms/diag_manager/test_diag_manager2.sh +++ b/test_fms/diag_manager/test_diag_manager2.sh @@ -460,6 +460,7 @@ test_expect_success "Unstructured grid (test $my_test_count)" ' mpirun -n 1 ../test_diag_manager ' +my_test_count=24 # test_diag_manager_time cat <<_EOF > diag_table test_diag_manager @@ -478,6 +479,7 @@ test_diag_manager "test_diag_manager_mod", "sst", "sst", "ocn_end%4yr%2mo%2dy%2hr", "all", .true., "none", 2 _EOF +my_test_count=25 rm -f input.nml && touch input.nml test_expect_success "wildcard filenames (test $my_test_count)" ' mpirun -n 1 ../test_diag_manager_time @@ -501,7 +503,7 @@ test_expect_success "diurnal test (test $my_test_count)" ' mpirun -n 1 ../test_diag_manager_time ' setup_test -my_test_count=`expr $my_test_count + 1` +my_test_count=26 test_expect_success "Test the diag update_buffer (test $my_test_count)" ' mpirun -n 1 ../test_diag_update_buffer ' diff --git a/test_fms/diag_manager/test_diag_update_buffer.F90 b/test_fms/diag_manager/test_diag_update_buffer.F90 index 67de3ec665..9ca4f93198 100644 --- a/test_fms/diag_manager/test_diag_update_buffer.F90 +++ b/test_fms/diag_manager/test_diag_update_buffer.F90 @@ -282,12 +282,12 @@ SUBROUTINE init_field_values (field) DO k = 1, NZ DO j = 1, NY DO i = 1, NX + itemp = get_array_index_from_4D(i,j,k,l,NX,NY,NZ) SELECT TYPE ( field) TYPE IS (real(kind=r4_kind)) - itemp = get_array_index_from_4D(i,j,k,l,NX,NY,NZ) - field(i,j,k,l) = get_array_index_from_4D(i,j,k,l,NX,NY,NZ) -1 TYPE IS (integer(kind=i8_kind)) - field(i,j,k,l) = get_array_index_from_4D(i,j,k,l,NX,NY,NZ) + field(i,j,k,l) = real(itemp, kind=r4_kind) + TYPE IS (integer(kind=i8_kind)) + field(i,j,k,l) = int(itemp, kind=i8_kind) END SELECT END DO END DO diff --git a/test_fms/drifters/test_cloud_interpolator.F90 b/test_fms/drifters/test_cloud_interpolator.F90 index e11d480587..204e5d545e 100644 --- a/test_fms/drifters/test_cloud_interpolator.F90 +++ b/test_fms/drifters/test_cloud_interpolator.F90 @@ -18,6 +18,7 @@ !*********************************************************************** program test_cloud_interpolator +#ifdef use_drifters use cloud_interpolator_mod use mpp_mod, only : mpp_error, FATAL, stdout, mpp_init, mpp_exit @@ -215,4 +216,5 @@ subroutine test_get_node_values end subroutine test_get_node_values +#endif end program test_cloud_interpolator diff --git a/test_fms/drifters/test_drifters.F90 b/test_fms/drifters/test_drifters.F90 index 0bfe4bb685..ab6cb91c89 100644 --- a/test_fms/drifters/test_drifters.F90 +++ b/test_fms/drifters/test_drifters.F90 @@ -18,6 +18,7 @@ !*********************************************************************** program test_drifters +#ifdef use_drifters !* contents of input file: drifters_inp_test_3d.nc !!$netcdf drifters_inp_test_3d { @@ -336,7 +337,7 @@ program test_drifters !#ifndef _SERIAL call mpp_exit !#endif - +#endif end program test_drifters subroutine my_error_handler(mesg) diff --git a/test_fms/drifters/test_drifters_comm.F90 b/test_fms/drifters/test_drifters_comm.F90 index 25b1e532d3..85108fa4d2 100644 --- a/test_fms/drifters/test_drifters_comm.F90 +++ b/test_fms/drifters/test_drifters_comm.F90 @@ -19,6 +19,7 @@ !********************************************************************** program test_drifters_comm +#ifdef use_drifters use drifters_core_mod use drifters_comm_mod @@ -143,4 +144,5 @@ program test_drifters_comm call mpp_domains_exit call mpp_exit +#endif end program test_drifters_comm diff --git a/test_fms/drifters/test_drifters_core.F90 b/test_fms/drifters/test_drifters_core.F90 index b2b93a5e82..51e81efc72 100644 --- a/test_fms/drifters/test_drifters_core.F90 +++ b/test_fms/drifters/test_drifters_core.F90 @@ -19,6 +19,7 @@ !********************************************************************** program test_drifters_core +#ifdef use_drifters use drifters_core_mod use fms_mod, only : fms_init, fms_end @@ -111,4 +112,5 @@ program test_drifters_core !!$ print *,'Sucessful test ier=', ier !!$ end if call fms_end() +#endif end program test_drifters_core diff --git a/test_fms/drifters/test_drifters_input.F90 b/test_fms/drifters/test_drifters_input.F90 index 64a887856d..297b751493 100644 --- a/test_fms/drifters/test_drifters_input.F90 +++ b/test_fms/drifters/test_drifters_input.F90 @@ -19,6 +19,7 @@ !*********************************************************************** program test_drifters_input +#ifdef use_drifters use drifters_input_mod use fms_mod, only : fms_init, fms_end use mpp_mod, only : mpp_error, FATAL, stdout @@ -54,4 +55,5 @@ program test_drifters_input call drifters_input_del(obj, ermesg) call fms_end() +#endif end program test_drifters_input diff --git a/test_fms/drifters/test_drifters_io.F90 b/test_fms/drifters/test_drifters_io.F90 index 989141b8f7..f74e8cb500 100644 --- a/test_fms/drifters/test_drifters_io.F90 +++ b/test_fms/drifters/test_drifters_io.F90 @@ -18,6 +18,7 @@ !*********************************************************************** program test_drifters_io +#ifdef use_drifters use drifters_io_mod use mpp_mod, only : mpp_error, FATAL, stdout, mpp_init, mpp_exit @@ -156,4 +157,5 @@ program test_drifters_io call mpp_error(FATAL, ermesg) endif call mpp_exit() +#endif end program test_drifters_io diff --git a/test_fms/drifters/test_quicksort.F90 b/test_fms/drifters/test_quicksort.F90 index 4ad7701a50..04cb268112 100644 --- a/test_fms/drifters/test_quicksort.F90 +++ b/test_fms/drifters/test_quicksort.F90 @@ -19,9 +19,11 @@ !*********************************************************************** program test_quicksort +#ifdef use_drifters implicit none integer :: list(16) = (/6, 2, 3, 4, 1, 45, 3432, 3245, 32545, 66555, 32, 1,3, -43254, 324, 54/) print *,'before list=', list call qksrt_quicksort(size(list), list, 1, size(list)) print *,'after list=', list +#endif end program test_quicksort diff --git a/test_fms/fms2_io/test_bc_restart.sh b/test_fms/fms2_io/test_bc_restart.sh index 07b0081c8f..faac53e0cb 100755 --- a/test_fms/fms2_io/test_bc_restart.sh +++ b/test_fms/fms2_io/test_bc_restart.sh @@ -43,7 +43,7 @@ test_expect_failure "bad checksum" ' ' # run test 3 - test for ignoring a bad checksum -printf "&test_bc_restart_nml\n bad_checksum=.true.\n ignore_checksum=.true./" | cat > input.nml +printf "&test_bc_restart_nml\n bad_checksum=.true.\n ignore_checksum=.true.\n /" | cat > input.nml test_expect_success "ignore bad checksum" ' mpirun -n 16 ../test_bc_restart ' diff --git a/test_fms/fms2_io/test_compressed_writes.F90 b/test_fms/fms2_io/test_compressed_writes.F90 index b905be70d7..38d44f6392 100644 --- a/test_fms/fms2_io/test_compressed_writes.F90 +++ b/test_fms/fms2_io/test_compressed_writes.F90 @@ -112,10 +112,10 @@ subroutine register_field_wrapper(fileob, var_name, dimension_names, ndim) character(len=*), intent(in) :: dimension_names(:) !< dimension names integer, intent(in) :: ndim !< Number of dimension - call register_field(fileob, trim(var_name)//"_r8", "double", names(1:ndim)) - call register_field(fileob, trim(var_name)//"_r4", "float", names(1:ndim)) - call register_field(fileob, trim(var_name)//"_i8", "int64", names(1:ndim)) - call register_field(fileob, trim(var_name)//"_i4", "int", names(1:ndim)) + call register_field(fileob, trim(var_name)//"_r8", "double", dimension_names(1:ndim)) + call register_field(fileob, trim(var_name)//"_r4", "float", dimension_names(1:ndim)) + call register_field(fileob, trim(var_name)//"_i8", "int64", dimension_names(1:ndim)) + call register_field(fileob, trim(var_name)//"_i4", "int", dimension_names(1:ndim)) end subroutine register_field_wrapper !> @brief Allocates the variable to be the size of data compute domain for x and y diff --git a/test_fms/fms2_io/test_domain_io.F90 b/test_fms/fms2_io/test_domain_io.F90 index 07a3e2845a..5b00d8c9fe 100644 --- a/test_fms/fms2_io/test_domain_io.F90 +++ b/test_fms/fms2_io/test_domain_io.F90 @@ -46,7 +46,7 @@ program test_domain_read integer :: xhalo = 3 !< Number of halo points in X integer :: yhalo = 2 !< Number of halo points in Y integer :: nz = 2 !< Number of points in the z dimension - character(len=20) :: filename="test.nc" !< Name of the file + character(len=32) :: filename="test.nc" !< Name of the file logical :: use_edges=.false. !< Use North and East domain positions integer :: ndim4 !< Number of points in dim4 @@ -64,7 +64,7 @@ program test_domain_read namelist /test_domain_io_nml/ layout, io_layout, nx, ny, nz, mask_table, xhalo, yhalo, nz, filename, use_edges - call fms_init + call fms_init() read(input_nml_file, nml=test_domain_io_nml, iostat=io) ierr = check_nml_error(io, 'test_domain_io_nml') @@ -134,7 +134,7 @@ program test_domain_read call close_file(fileobj) endif - call fms_end + call fms_end() contains @@ -146,10 +146,10 @@ subroutine register_field_wrapper(fileob, var_name, dimension_names, ndim) character(len=*), intent(in) :: dimension_names(:) !< dimension names integer, intent(in) :: ndim !< Number of dimension - call register_field(fileob, trim(var_name)//"_r8", "double", names(1:ndim)) - call register_field(fileob, trim(var_name)//"_r4", "float", names(1:ndim)) - call register_field(fileob, trim(var_name)//"_i8", "int", names(1:ndim)) - call register_field(fileob, trim(var_name)//"_i4", "int64", names(1:ndim)) + call register_field(fileob, trim(var_name)//"_r8", "double", dimension_names(1:ndim)) + call register_field(fileob, trim(var_name)//"_r4", "float", dimension_names(1:ndim)) + call register_field(fileob, trim(var_name)//"_i8", "int", dimension_names(1:ndim)) + call register_field(fileob, trim(var_name)//"_i4", "int64", dimension_names(1:ndim)) end subroutine register_field_wrapper !> @brief Allocates the variable to be the size of data compute domain for x and y diff --git a/test_fms/mpp/test_mpp_clock_begin_end_id.sh b/test_fms/mpp/test_mpp_clock_begin_end_id.sh index 39e5f3ffee..beeeb287e1 100755 --- a/test_fms/mpp/test_mpp_clock_begin_end_id.sh +++ b/test_fms/mpp/test_mpp_clock_begin_end_id.sh @@ -26,6 +26,7 @@ # Set common test settings. . ../test-lib.sh +touch input.nml touch clock.nml echo "&test_mpp_clock_begin_end_id_nml" > clock.nml echo "test_number = 0" >> clock.nml diff --git a/test_fms/mpp/test_mpp_nesting.F90 b/test_fms/mpp/test_mpp_nesting.F90 index 833c580bf5..8808f0e9c8 100644 --- a/test_fms/mpp/test_mpp_nesting.F90 +++ b/test_fms/mpp/test_mpp_nesting.F90 @@ -950,15 +950,15 @@ subroutine test_update_nest_domain_r8( type ) !--- loop over nest level do l = 1, num_nest_level npes_my_level = mpp_get_nest_npes(nest_domain, l) - npes_my_fine = mpp_get_nest_fine_npes(nest_domain,l) allocate(my_pelist(npes_my_level)) - allocate(my_pelist_fine(npes_my_fine)) call mpp_get_nest_pelist(nest_domain, l, my_pelist) call mpp_declare_pelist(my_pelist(:)) write(type2, '(a,I2)')trim(type)//" nest_level = ",l if(ANY(my_pelist(:)==mpp_pe())) then + npes_my_fine = mpp_get_nest_fine_npes(nest_domain,l) + allocate(my_pelist_fine(npes_my_fine)) call mpp_get_nest_fine_pelist(nest_domain, l, my_pelist_fine) call mpp_set_current_pelist(my_pelist) @@ -2625,7 +2625,8 @@ subroutine test_update_nest_domain_r8( type ) deallocate(wbuffery2, ebuffery2, sbuffery2, nbuffery2) endif endif - deallocate(my_pelist, my_pelist_fine) + if(ANY(my_pelist(:)==mpp_pe())) deallocate(my_pelist_fine) + deallocate(my_pelist) call mpp_set_current_pelist() enddo @@ -2984,15 +2985,15 @@ subroutine test_update_nest_domain_r4( type ) !--- loop over nest level do l = 1, num_nest_level npes_my_level = mpp_get_nest_npes(nest_domain, l) - npes_my_fine = mpp_get_nest_fine_npes(nest_domain,l) allocate(my_pelist(npes_my_level)) - allocate(my_pelist_fine(npes_my_fine)) call mpp_get_nest_pelist(nest_domain, l, my_pelist) call mpp_declare_pelist(my_pelist(:)) write(type2, '(a,I2)')trim(type)//" nest_level = ",l if(ANY(my_pelist(:) == mpp_pe())) then + npes_my_fine = mpp_get_nest_fine_npes(nest_domain,l) + allocate(my_pelist_fine(npes_my_fine)) call mpp_get_nest_fine_pelist(nest_domain, l, my_pelist_fine) call mpp_set_current_pelist(my_pelist) @@ -4655,7 +4656,8 @@ subroutine test_update_nest_domain_r4( type ) endif endif - deallocate(my_pelist, my_pelist_fine) + if(ANY(my_pelist(:)==mpp_pe())) deallocate(my_pelist_fine) + deallocate(my_pelist) call mpp_set_current_pelist() enddo diff --git a/test_fms/mpp/test_read_input_nml.F90 b/test_fms/mpp/test_read_input_nml.F90 index 15e6e4697b..9a28e601fe 100644 --- a/test_fms/mpp/test_read_input_nml.F90 +++ b/test_fms/mpp/test_read_input_nml.F90 @@ -68,7 +68,7 @@ program test_read_input_nml n = 1 do read(1, '(A)', iostat=stat) line - if (stat.eq.-1) then + if (is_iostat_end(stat)) then exit end if if (input_nml_file(n).ne.line) then diff --git a/test_fms/mpp/test_read_input_nml2.sh b/test_fms/mpp/test_read_input_nml2.sh index 5750f641b9..5eb411429b 100755 --- a/test_fms/mpp/test_read_input_nml2.sh +++ b/test_fms/mpp/test_read_input_nml2.sh @@ -31,11 +31,11 @@ # create and enter directory for in/output output_dir -touch input.nml touch test_numb_base.nml echo "&test_read_input_nml_nml" > test_numb_base.nml echo "test_numb = 0" >> test_numb_base.nml echo "/" >> test_numb_base.nml +cp test_numb_base.nml input.nml # Test 1 sed "s/test_numb = [0-9]/test_numb = 1/" test_numb_base.nml > test_numb.nml @@ -45,7 +45,7 @@ test_expect_success "read input nml" ' # Test 2 sed "s/test_numb = [0-9]/test_numb = 2/" test_numb_base.nml > test_numb.nml -sed "s/1/2/" $top_srcdir/test_fms/mpp/input_base.nml > input_alternative.nml +cp input.nml input_alternative.nml test_expect_success "read input nml with file name" ' mpirun -n 1 ../test_read_input_nml ' diff --git a/test_fms/sat_vapor_pres/test_sat_vapor_pres.F90 b/test_fms/sat_vapor_pres/test_sat_vapor_pres.F90 index a05fc9811c..7d3ae2d834 100644 --- a/test_fms/sat_vapor_pres/test_sat_vapor_pres.F90 +++ b/test_fms/sat_vapor_pres/test_sat_vapor_pres.F90 @@ -52,8 +52,8 @@ program test_sat_vap_pressure real(r8_kind), dimension(:), allocatable :: TABLE, DTABLE, TABLE2, DTABLE2, TABLE3, DTABLE3 integer :: io, N -integer, parameter :: nml_unit_var=100 -character(100) :: nml_file +integer :: nml_unit_var +character(*), parameter :: nml_file = 'test_sat_vapor_pres.nml' logical :: test1, test2, test3, test4, test5 NAMELIST / test_sat_vapor_pres_nml/ test1, test2, test3, test4, test5 @@ -64,8 +64,7 @@ program test_sat_vap_pressure call sat_vapor_pres_init() !> compute tables to be used for testing call compute_tables() !> compute tables to generate answers/reference values -nml_file='test_sat_vapor_pres.nml' -open(unit=nml_unit_var, file=trim(nml_file), action='read') +open(newunit=nml_unit_var, file=nml_file, action='read') read(unit=nml_unit_var, nml=test_sat_vapor_pres_nml,iostat=io) close(nml_unit_var)